[
  {
    "path": ".gitignore",
    "content": "# maven ignore\ntarget/\n*.jar\n*.war\n*.zip\n*.tar\n*.tar.gz\n\n# eclipse ignore\n.settings/\n.project\n.classpath\n\n# idea ignore\n.idea/\n*.ipr\n*.iml\n*.iws\n\n# temp ignore\n*.log\n*.cache\n*.diff\n*.patch\n*.tmp\n\n# system ignore\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "README.md",
    "content": "# tyloo\nDistributed transaction framework——TCC\n\n\n\n## 概念\n\n- Try: 尝试执行业务\n  - 完成所有业务检查（一致性）\n  - 预留必须业务资源（准隔离性）\n\n- Confirm: 确认执行业务\n  - 真正执行业务\n  - 不作任何业务检查\n  - 只使用Try阶段预留的业务资源\n  - Confirm操作满足幂等性 \n\n- Cancel: 取消执行业务\n\n  - 释放Try阶段预留的业务资源\n  - Cancel操作满足幂等性\n\n  \n\n\n\n## 环境\n\n- Java\n- Maven\n- Git\n- MySQL\n- Redis\n- Zookeeper\n- Intellij IDEA\n\n\n\n\n\n## 功能\n\n- 基于 Spring AOP 切面思想实现对分布式事务注解的拦截。\n- 基于Dubbo的ProxyFactory代理机制为服务接口生成代理对象。\n- 基于Mysql、Redis乐观锁进行事务版本控制以及基于基于Quartz进行事务恢复。\n- 支持多种事务日志序列化以及事务存储器实现。\n- 调用方式（版本）：Dubbo、HTTP\n\n\n\n\n\n## 业务场景\n\n- https://www.cnblogs.com/jajian/p/10014145.html\n\n- **我们有必要使用TCC分布式事务机制来保证各个服务形成一个整体性的事务**\n\n\n\n\n\n\n\n## 运行\n\n1. 导入数据库脚本`tyloo/tyloo-tutorial-sample/src/tylooSampledb`\n2. 修改三个配置文件`tccjdbc.properties`\n3. 修改三个子项目（capital、redpacket、order）的启动配置：HTTP port\n\n\n\n\n\n\n\n## 模块\n\n- **两个拦截器**\n\n  - 通过对 @Tyloo AOP 切面( 参与者 try 方法 )进行拦截，透明化对参与者confirm / cancel 方法调用，从而实现 TCC\n\n- **事务与参与者**\n\n  - TCC 通过多个参与者的 try / confirm / cancel 方法，实现事务的最终一致性\n  - Tyloo 将每个业务操作抽象成事务参与者\n\n- **事务管理器**\n\n  - 提供事务的获取、发起、提交、回滚，参与者的新增等等方法。\n\n- **事务存储器**\n\n  - 提供对事务对象的持久化\n\n- **事务注解**\n\n  - 传播级别\n  - 确认/取消执行业务方法\n  - 事务上下文编辑\n\n- **事务恢复**\n\n  - 事务信息被持久化到外部的存储器中。事务存储是事务恢复的基础。通过读取外部存储器中的异常事务，定时任务会按照一定频率对事务进行重试，直到事务完成或超过最大重试次数。\n\n  \n\n\n\n\n\n## 流程\n\n- 因为对远程业务的调用需要用到代理对象，代理对象由dubbo service生成，TRY阶段在进行远程调用前需要调用代理对象（代理对象的confrimmethod和cancelmethod均和try方法名字相同），此时拦截器会进行拦截代理对象，拦截后调用远程业务，远程业务（远程业务本地有个本地事务保证执行成功）也会被拦截，然后远程业务执行。COMMIT阶段时主业务执行完comfirm方法后代理对象执行comfirm方法（confirm方法均为反射调用），但是代理对象的confirm方法还是代理对象的try方法，此时再被拦截器拦截，因为此时代理对象的事务状态已经改为CONFIRMING，由于事务类型为defalut（因为代理对象的传播级别默认为SUPPORT），所以直接过了。此时再调用远程业务的try方法，走拦截，invoke反射的时候反射远程业务的confrim方法，因为try方法做了幂等所以直接过了，此时远程业务的commit阶段完成，然后继续下一个远程业务。最后根事务提交，完成。\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>io.tyloo</groupId>\n    <artifactId>tyloo</artifactId>\n    <packaging>pom</packaging>\n    <version>1.1.0</version>\n\n    <modules>\n        <module>tyloo-api</module>\n        <module>tyloo-core</module>\n        <module>tyloo-spring</module>\n        <module>tyloo-unit-test</module>\n        <module>tyloo-tutorial-sample</module>\n        <module>tyloo-server</module>\n        <module>tyloo-dubbo</module>\n        <module>tyloo-bom</module>\n    </modules>\n\n    <properties>\n\n        <java.src.version>1.8</java.src.version>\n        <java.target.version>1.8</java.target.version>\n        <project.encoding>GBK</project.encoding>\n        <springframework.version>4.3.18.RELEASE</springframework.version>\n        <aspectj.version>1.9.2</aspectj.version>\n        <slf4j.version>1.7.9</slf4j.version>\n        <dubbo.version>2.7.4.1</dubbo.version>\n\n    </properties>\n\n\n    <dependencyManagement>\n\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-framework-bom</artifactId>\n                <version>${springframework.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>com.fasterxml.jackson</groupId>\n                <artifactId>jackson-bom</artifactId>\n                <version>2.10.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.dubbo</groupId>\n                <artifactId>dubbo</artifactId>\n                <version>${dubbo.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.dubbo</groupId>\n                <artifactId>dubbo-dependencies-zookeeper</artifactId>\n                <version>${dubbo.version}</version>\n                <type>pom</type>\n            </dependency>\n\n            <dependency>\n                <groupId>com.mchange</groupId>\n                <artifactId>c3p0</artifactId>\n                <version>0.9.5.4</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.quartz-scheduler</groupId>\n                <artifactId>quartz</artifactId>\n                <version>2.3.2</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.esotericsoftware</groupId>\n                <artifactId>kryo</artifactId>\n                <version>4.0.2</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>19.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.httpcomponents</groupId>\n                <artifactId>httpclient</artifactId>\n                <version>4.5.10</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>1.2.62</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.aspectj</groupId>\n                <artifactId>aspectjrt</artifactId>\n                <version>${aspectj.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.aspectj</groupId>\n                <artifactId>aspectjweaver</artifactId>\n                <version>${aspectj.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-lang3</artifactId>\n                <version>3.9</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-api</artifactId>\n                <version>${slf4j.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>jcl-over-slf4j</artifactId>\n                <version>${slf4j.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-log4j12</artifactId>\n                <version>${slf4j.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>log4j</groupId>\n                <artifactId>log4j</artifactId>\n                <version>1.2.17</version>\n            </dependency>\n\n            <dependency>\n                <groupId>mysql</groupId>\n                <artifactId>mysql-connector-java</artifactId>\n                <version>5.1.48</version>\n            </dependency>\n\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>4.12</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mybatis</groupId>\n                <artifactId>mybatis</artifactId>\n                <version>3.2.8</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mybatis</groupId>\n                <artifactId>mybatis-spring</artifactId>\n                <version>1.2.2</version>\n            </dependency>\n\n            <dependency>\n                <groupId>redis.clients</groupId>\n                <artifactId>jedis</artifactId>\n                <version>2.9.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.freemarker</groupId>\n                <artifactId>freemarker</artifactId>\n                <version>2.3.29</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-codec</groupId>\n                <artifactId>commons-codec</artifactId>\n                <version>1.11</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.tyloo</groupId>\n                <artifactId>tyloo-api</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.tyloo</groupId>\n                <artifactId>tyloo-core</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n        </dependencies>\n\n    </dependencyManagement>\n\n\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <filtering>true</filtering>\n            </resource>\n        </resources>\n        <testResources>\n            <testResource>\n                <directory>src/test/resources</directory>\n                <filtering>true</filtering>\n            </testResource>\n        </testResources>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-resources-plugin</artifactId>\n                <version>2.4</version>\n                <configuration>\n                    <encoding>${project.encoding}</encoding>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>2.0.2</version>\n                <configuration>\n                    <source>${java.src.version}</source>\n                    <target>${java.target.version}</target>\n                    <encoding>${project.encoding}</encoding>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-eclipse-plugin</artifactId>\n                <version>2.6</version>\n                <configuration>\n                    <workspace>${basedir}</workspace>\n                    <workspaceCodeStylesURL>\n                        http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-eclipse-plugin/src/optional/eclipse-config/maven-styles.xml\n                    </workspaceCodeStylesURL>\n                    <downloadSources>true</downloadSources>\n                    <downloadJavadocs>false</downloadJavadocs>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>2.2.1</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <version>2.7</version>\n                <configuration>\n                    <updateReleaseInfo>true</updateReleaseInfo>\n                </configuration>\n            </plugin>\n        </plugins>\n\n    </build>\n\n\n</project>"
  },
  {
    "path": "tyloo-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-api</artifactId>\n    <dependencies>\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n            <version>5.2.5</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/Propagation.java",
    "content": "package io.tyloo.api;\n\n/*\n *\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 11:49 2019/4/4\n *\n */\n\npublic enum Propagation {\n    /**\n     * ֵ֧ǰǰû񣬾½һ\n     */\n    REQUIRED(0),\n    /**\n     * ֵ֧ǰǰû񣬾Էʽִ\n     */\n    SUPPORTS(1),\n    /**\n     * ֵ֧ǰǰû񣬾׳쳣\n     */\n    MANDATORY(2),\n    /**\n     * ½ǰ񣬰ѵǰ\n     */\n    REQUIRES_NEW(3);\n\n    private final int value;\n\n    Propagation(int value) {\n        this.value = value;\n    }\n\n    public int value() {\n        return this.value;\n    }\n}"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/TransactionContext.java",
    "content": "package io.tyloo.api;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/*\n *\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 12:01 2019/4/6\n *\n */\npublic class TransactionContext implements Serializable {\n\n    private static final long serialVersionUID = -8199390103169700387L;\n\n    private TransactionXid xid;\n\n    private int status;\n\n    private Map<String, String> attachments = new ConcurrentHashMap<String, String>();\n\n    public TransactionContext() {\n\n    }\n\n    public TransactionContext(TransactionXid xid, int status) {\n        this.xid = xid;\n        this.status = status;\n    }\n\n    public void setXid(TransactionXid xid) {\n        this.xid = xid;\n    }\n\n    public TransactionXid getXid() throws CloneNotSupportedException {\n        return xid.clone();\n    }\n\n    public void setAttachments(Map<String, String> attachments) {\n        if (attachments != null && !attachments.isEmpty()) {\n            this.attachments.putAll(attachments);\n        }\n    }\n\n    public Map<String, String> getAttachments() {\n        return attachments;\n    }\n\n    public void setStatus(int status) {\n        this.status = status;\n    }\n\n    public int getStatus() {\n        return status;\n    }\n\n\n}\n"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/TransactionContextEditor.java",
    "content": "package io.tyloo.api;\n\nimport java.lang.reflect.Method;\n\n/*\n *\n * ı༭úͻ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 11:35 2019/4/7\n *\n */\n\npublic interface TransactionContextEditor {\n    /**\n     * Ӳл\n     *\n     * @param target \n     * @param method \n     * @param args   \n     * @return \n     */\n\n    TransactionContext get(Object target, Method method, Object[] args);\n    /**\n     * ĵ\n     *\n     * @param transactionContext \n     * @param target             \n     * @param method             \n     * @param args               \n     */\n    void set(TransactionContext transactionContext, Object target, Method method, Object[] args);\n\n}\n"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/TransactionStatus.java",
    "content": "package io.tyloo.api;\n\n/*\n *\n * ״̬ö\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 12:05 2019/4/5\n *\n */\npublic enum TransactionStatus {\n\n    /**\n     * try׶\n     */\n    TRYING(1),\n\n    /**\n     * confirm׶\n     */\n    CONFIRMING(2),\n\n    /**\n     * cancel׶\n     */\n    CANCELLING(3);\n\n    private int id;\n\n     TransactionStatus(int id) {\n        this.id = id;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public static TransactionStatus valueOf(int id) {\n\n        switch (id) {\n            case 1:\n                return TRYING;\n            case 2:\n                return CONFIRMING;\n            default:\n                return CANCELLING;\n        }\n    }\n\n}\n"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/TransactionXid.java",
    "content": "package io.tyloo.api;\n\n\nimport javax.transaction.xa.Xid;\nimport java.io.Serializable;\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\n\nimport cn.hutool.core.lang.UUID;\n\n/*\n * xidţΨһʶһʹ UUID 㷨ɣ֤Ψһԡ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 14:57 2019/4/10\n *\n */\npublic class TransactionXid implements Xid, Serializable {\n\n    private static final long serialVersionUID = -6817267250789142043L;\n    /**\n     * XID ĸʽʶ\n     * һ֣ڱʶglobalTransactionIdbranchQualifierֵʹõĸʽĬֵ1\n     */\n    private int formatId = 1;\n    /**\n     * ȫID.\n     * ͬķֲʽӦʹͬglobalTransactionIdȷ֪XAĸֲʽ\n     */\n    private byte[] globalTransactionId;\n    /**\n     * ֧޶.\n     * Ĭֵǿմһֲʽеÿ֧bqualֵΨһ\n     */\n    private byte[] branchQualifier;\n\n    private static byte[] CUSTOMIZED_TRANSACTION_ID = \"UniqueIdentity\".getBytes();\n\n    public TransactionXid() {\n        globalTransactionId = uuidToByteArray(UUID.randomUUID());\n        branchQualifier = uuidToByteArray(UUID.randomUUID());\n    }\n\n    public void setGlobalTransactionId(byte[] globalTransactionId) {\n        this.globalTransactionId = globalTransactionId;\n    }\n\n    public void setBranchQualifier(byte[] branchQualifier) {\n        this.branchQualifier = branchQualifier;\n    }\n\n    public TransactionXid(Object uniqueIdentity) {\n\n        if (uniqueIdentity == null) {\n\n            globalTransactionId = uuidToByteArray(UUID.randomUUID());\n            branchQualifier = uuidToByteArray(UUID.randomUUID());\n\n        } else {\n\n            this.globalTransactionId = CUSTOMIZED_TRANSACTION_ID;\n\n            this.branchQualifier = uniqueIdentity.toString().getBytes();\n        }\n    }\n\n    public TransactionXid(byte[] globalTransactionId) {\n        this.globalTransactionId = globalTransactionId;\n        this.branchQualifier = uuidToByteArray(UUID.randomUUID());\n    }\n\n    public TransactionXid(byte[] globalTransactionId, byte[] branchQualifier) {\n        this.globalTransactionId = globalTransactionId;\n        this.branchQualifier = branchQualifier;\n    }\n\n    /**\n     * ȡ XID ĸʽʶ֡\n     */\n    @Override\n    public int getFormatId() {\n        return formatId;\n    }\n\n    /**\n     * ȡ XID ȫʶΪֽ顣\n     */\n    @Override\n    public byte[] getGlobalTransactionId() {\n        return globalTransactionId;\n    }\n\n    /**\n     * ȡ XID ֧ʶΪֽ顣\n     */\n    @Override\n    public byte[] getBranchQualifier() {\n        return branchQualifier;\n    }\n\n    @Override\n    public String toString() {\n\n        StringBuilder stringBuilder = new StringBuilder();\n        if (Arrays.equals(CUSTOMIZED_TRANSACTION_ID, globalTransactionId)) {\n\n            stringBuilder.append(new String(globalTransactionId));\n            stringBuilder.append(\":\").append(new String(branchQualifier));\n\n        } else {\n\n            stringBuilder.append(UUID.nameUUIDFromBytes(globalTransactionId).toString());\n            stringBuilder.append(\":\").append(UUID.nameUUIDFromBytes(branchQualifier).toString());\n        }\n\n        return stringBuilder.toString();\n    }\n\n    @Override\n    public TransactionXid clone() throws CloneNotSupportedException {\n\n        TransactionXid clone = (TransactionXid) super.clone();\n\n        byte[] cloneGlobalTransactionId = null;\n        byte[] cloneBranchQualifier = null;\n\n        if (globalTransactionId != null) {\n            cloneGlobalTransactionId = new byte[globalTransactionId.length];\n            System.arraycopy(globalTransactionId, 0, cloneGlobalTransactionId, 0, globalTransactionId.length);\n        }\n\n        if (branchQualifier != null) {\n            cloneBranchQualifier = new byte[branchQualifier.length];\n            System.arraycopy(branchQualifier, 0, cloneBranchQualifier, 0, branchQualifier.length);\n        }\n\n        return new TransactionXid(cloneGlobalTransactionId, cloneBranchQualifier);\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + this.getFormatId();\n        result = prime * result + Arrays.hashCode(branchQualifier);\n        result = prime * result + Arrays.hashCode(globalTransactionId);\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        return this == obj;\n\n    }\n\n    private static byte[] uuidToByteArray(UUID uuid) {\n        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);\n        bb.putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits());\n        return bb.array();\n    }\n\n}\n\n\n"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/Tyloo.java",
    "content": "package io.tyloo.api;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\n/*\n *\n * ע\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 11:32 2019/4/1\n *\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.METHOD})\npublic @interface Tyloo {\n\n    /**\n     * \n     */\n    Propagation propagation() default Propagation.REQUIRED;\n\n    /**\n     * ȷִҵ񷽷\n     */\n    String confirmMethod() default \"\";\n\n    /**\n     * ȡִҵ񷽷\n     */\n    String cancelMethod() default \"\";\n\n    /**\n     * ı༭\n     */\n    public Class<? extends TransactionContextEditor> transactionContextEditor() default DefaultTransactionContextEditor.class;\n\n    /**\n     * ʱ쳣\n     * delayCancelExceptions()ʾϵͳõ쳣ʱrollbacɻָjobִָk\n     * ͨҪʱ쳣ΪdelayCancelExceptionsԱΪʱ˳ʱ쳣rollback, Ǵûִ꣬Ӷrollbackʧ\n     *\n     * @return\n     */\n    public Class<? extends Exception>[] delayCancelExceptions() default {};\n\n    public boolean asyncConfirm() default false;\n\n    public boolean asyncCancel() default false;\n\n    /**\n     * Ĭı༭ʵ\n     */\n    class DefaultTransactionContextEditor implements TransactionContextEditor {\n\n        @Override\n        public TransactionContext get(Object target, Method method, Object[] args) {\n            int position = getTransactionContextParamPosition(method.getParameterTypes());\n\n            if (position >= 0) {\n                return (TransactionContext) args[position];\n            }\n\n            return null;\n        }\n\n        @Override\n        public void set(TransactionContext transactionContext, Object target, Method method, Object[] args) {\n\n            int position = getTransactionContextParamPosition(method.getParameterTypes());\n            if (position >= 0) {\n                args[position] = transactionContext;\n            }\n        }\n\n        /**\n         * ڷλ\n         *\n         * @param parameterTypes ͼ\n         * @return λ\n         */\n        static int getTransactionContextParamPosition(Class<?>[] parameterTypes) {\n\n            int position = -1;\n\n            for (int i = 0; i < parameterTypes.length; i++) {\n                if (parameterTypes[i].equals(TransactionContext.class)) {\n                    position = i;\n                    break;\n                }\n            }\n            return position;\n        }\n\n        /**\n         * @param args б\n         * @return ȡTransactionContext\n         */\n        public static TransactionContext getTransactionContextFromArgs(Object[] args) {\n\n            TransactionContext transactionContext = null;\n\n            for (Object arg : args) {\n                if (arg != null && TransactionContext.class.isAssignableFrom(arg.getClass())) {\n\n                    transactionContext = (TransactionContext) arg;\n                }\n            }\n\n            return transactionContext;\n        }\n    }\n}"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/UniqueIdentity.java",
    "content": "package io.tyloo.api;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 16:40 2019/4/6\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.PARAMETER})\npublic @interface UniqueIdentity {\n}\n"
  },
  {
    "path": "tyloo-api/src/main/java/io/tyloo/api/UuidUtils.java",
    "content": "package io.tyloo.api;\n\nimport java.nio.ByteBuffer;\n\nimport cn.hutool.core.lang.UUID;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:06 2019/4/4\n *\n */\n\npublic class UuidUtils {\n\n    public static byte[] uuidToByteArray(UUID uuid) {\n        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);\n        bb.putLong(uuid.getMostSignificantBits());\n        bb.putLong(uuid.getLeastSignificantBits());\n        return bb.array();\n    }\n\n    public static UUID byteArrayToUUID(byte[] bytes) {\n        ByteBuffer bb = ByteBuffer.wrap(bytes);\n        long firstLong = bb.getLong();\n        long secondLong = bb.getLong();\n        return new UUID(firstLong, secondLong);\n    }\n}\n\n"
  },
  {
    "path": "tyloo-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-core</artifactId>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n\n\n\n        <dependency>\n            <groupId>com.esotericsoftware</groupId>\n            <artifactId>kryo</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/CancellingException.java",
    "content": "package io.tyloo;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:55 2019/6/1\n *\n */\npublic class CancellingException extends RuntimeException {\n\n\n    public CancellingException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/ConcurrentTransactionException.java",
    "content": "package io.tyloo;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:55 2019/6/1\n *\n */\npublic class ConcurrentTransactionException extends RuntimeException {\n    private static final long serialVersionUID = 4099060614687283527L;\n\n    public ConcurrentTransactionException() {\n\n    }\n\n    public ConcurrentTransactionException(String message) {\n        super(message);\n    }\n\n    public ConcurrentTransactionException(String message, Throwable e) {\n        super(message, e);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/ConfirmingException.java",
    "content": "package io.tyloo;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:55 2019/6/1\n *\n */\npublic class ConfirmingException extends RuntimeException {\n\n    public ConfirmingException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/InvocationContext.java",
    "content": "package io.tyloo;\n\nimport java.io.Serializable;\n\n/*\n *\n *  ִз\n *  ¼ࡢ顢顣\n *  ͨЩԣִύ / ع\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:48 2019/6/3\n *\n */\npublic class InvocationContext implements Serializable {\n\n    private static final long serialVersionUID = -7969140711432461165L;\n\n    /**\n     * \n     */\n    private Class targetClass;\n\n    /**\n     * \n     */\n    private String methodName;\n\n    /**\n     * \n     */\n    private Class[] parameterTypes;\n\n    /**\n     * \n     */\n    private Object[] args;\n\n    public InvocationContext() {\n\n    }\n\n    public InvocationContext(Class targetClass, String methodName, Class[] parameterTypes, Object... args) {\n        this.methodName = methodName;\n        this.parameterTypes = parameterTypes;\n        this.targetClass = targetClass;\n        this.args = args;\n    }\n\n    public Object[] getArgs() {\n        return args;\n    }\n\n    public Class getTargetClass() {\n        return targetClass;\n    }\n\n    public String getMethodName() {\n        return methodName;\n    }\n\n    public Class[] getParameterTypes() {\n        return parameterTypes;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/NoExistedTransactionException.java",
    "content": "package io.tyloo;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:55 2019/6/1\n *\n */\npublic class NoExistedTransactionException extends Exception {\n    private static final long serialVersionUID = 1031919168789207713L;\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/OptimisticLockException.java",
    "content": "package io.tyloo;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:55 2019/6/1\n *\n */\npublic class OptimisticLockException extends RuntimeException {\n    private static final long serialVersionUID = -4250747002159020605L;\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/Participant.java",
    "content": "package io.tyloo;\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionContextEditor;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.api.TransactionXid;\n\nimport java.io.Serializable;\n\n/*\n *\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:00 2019/6/5\n *\n */\n\npublic class Participant implements Serializable {\n\n    private static final long serialVersionUID = 4127729421281425247L;\n\n    private TransactionXid xid;\n\n    private InvocationContext confirmInvocationContext;\n\n    private InvocationContext cancelInvocationContext;\n\n    Class<? extends TransactionContextEditor> transactionContextEditorClass;\n\n    public Participant() {\n\n    }\n\n    public Participant(TransactionXid xid, InvocationContext confirmInvocationContext, InvocationContext cancelInvocationContext, Class<? extends TransactionContextEditor> transactionContextEditorClass) {\n        this.xid = xid;\n        this.confirmInvocationContext = confirmInvocationContext;\n        this.cancelInvocationContext = cancelInvocationContext;\n        this.transactionContextEditorClass = transactionContextEditorClass;\n    }\n\n    public Participant(InvocationContext confirmInvocationContext, InvocationContext cancelInvocationContext, Class<? extends TransactionContextEditor> transactionContextEditorClass) {\n        this.confirmInvocationContext = confirmInvocationContext;\n        this.cancelInvocationContext = cancelInvocationContext;\n        this.transactionContextEditorClass = transactionContextEditorClass;\n    }\n\n    public void setXid(TransactionXid xid) {\n        this.xid = xid;\n    }\n\n    public void rollback() {\n        Terminator.invoke(new TransactionContext(xid, TransactionStatus.CANCELLING.getId()), cancelInvocationContext, transactionContextEditorClass);\n    }\n\n    public void commit() {\n        Terminator.invoke(new TransactionContext(xid, TransactionStatus.CONFIRMING.getId()), confirmInvocationContext, transactionContextEditorClass);\n    }\n\n    public TransactionXid getXid() {\n        return xid;\n    }\n\n    public InvocationContext getConfirmInvocationContext() {\n        return confirmInvocationContext;\n    }\n\n    public InvocationContext getCancelInvocationContext() {\n        return cancelInvocationContext;\n    }\n\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/SystemException.java",
    "content": "package io.tyloo;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:55 2019/6/1\n *\n */\npublic class SystemException extends RuntimeException {\n\n    public SystemException(String message) {\n        super(message);\n    }\n\n    public SystemException(Throwable e) {\n        super(e);\n    }\n\n    public SystemException(String message, Throwable e) {\n        super(message, e);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/Terminator.java",
    "content": "package io.tyloo;\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionContextEditor;\nimport io.tyloo.support.FactoryBuilder;\nimport io.tyloo.utils.StringUtils;\n\nimport java.lang.reflect.Method;\n\n/*\n *\n * ִ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:01 2019/6/6\n *\n */\n\npublic final class Terminator {\n\n    public Terminator() {\n\n    }\n\n    /**\n     * ݵģȡĿ귽ִз.\n     *\n     * @param invocationContext\n     */\n    public static void invoke(TransactionContext transactionContext, InvocationContext invocationContext, Class<? extends TransactionContextEditor> transactionContextEditorClass) {\n\n\n        if (StringUtils.isNotEmpty(invocationContext.getMethodName())) {\n\n            try {\n\n                Object target = FactoryBuilder.factoryOf(invocationContext.getTargetClass()).getInstance();\n\n                Method method = null;\n\n                //ע\n                method = target.getClass().getMethod(invocationContext.getMethodName(), invocationContext.getParameterTypes());\n\n                FactoryBuilder.factoryOf(transactionContextEditorClass).getInstance().set(transactionContext, target, method, invocationContext.getArgs());\n\n                // ÷񷽷ٴαTylooAspectTylooCoordinatorAspectأΪ״̬ѾTRYINGˣֱִԶ̷\n                method.invoke(target, invocationContext.getArgs());\n\n            } catch (Exception e) {\n                throw new SystemException(e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/Transaction.java",
    "content": "package io.tyloo;\n\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.api.TransactionXid;\nimport io.tyloo.common.TransactionType;\n\nimport javax.transaction.xa.Xid;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 18:04 2019/4/30\n *\n */\n\npublic class Transaction implements Serializable  {\n\n    private static final long serialVersionUID = 7291423944314337931L;\n\n    private TransactionXid xid;\n\n    private TransactionStatus status;\n\n    private TransactionType transactionType;\n\n    private volatile int retriedCount = 0;\n\n    private Date createTime = new Date();\n\n    private Date lastUpdateTime = new Date();\n\n    private long version = 1;\n\n    private List<Participant> participants = new ArrayList<Participant>();\n\n    private Map<String, Object> attachments = new ConcurrentHashMap<String, Object>();\n\n    public Transaction() {\n\n    }\n\n    public Transaction(TransactionContext transactionContext) throws CloneNotSupportedException {\n        this.xid = transactionContext.getXid();\n        this.status = TransactionStatus.TRYING;\n        this.transactionType = TransactionType.BRANCH;\n    }\n\n    public Transaction(TransactionType transactionType) {\n        this.xid = new TransactionXid();\n        this.status = TransactionStatus.TRYING;\n        this.transactionType = transactionType;\n    }\n\n    public Transaction(Object uniqueIdentity,TransactionType transactionType) {\n\n        this.xid = new TransactionXid(uniqueIdentity);\n        this.status = TransactionStatus.TRYING;\n        this.transactionType = transactionType;\n    }\n\n    public void enlistParticipant(Participant participant) {\n        participants.add(participant);\n    }\n\n    public Xid getXid() {\n        try {\n            return xid.clone();\n        } catch (CloneNotSupportedException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    public TransactionStatus getStatus() {\n        return status;\n    }\n\n\n    public List<Participant> getParticipants() {\n        return participants;\n    }\n\n    public TransactionType getTransactionType() {\n        return transactionType;\n    }\n\n    public void changeStatus(TransactionStatus status) {\n        this.status = status;\n    }\n\n\n    public void commit() {\n\n        for (Participant participant : participants) {\n            participant.commit();\n        }\n    }\n\n    public void rollback() {\n        for (Participant participant : participants) {\n            participant.rollback();\n        }\n    }\n\n    public int getRetriedCount() {\n        return retriedCount;\n    }\n\n    public void addRetriedCount() {\n        this.retriedCount++;\n    }\n\n    public void resetRetriedCount(int retriedCount) {\n        this.retriedCount = retriedCount;\n    }\n\n    public Map<String, Object> getAttachments() {\n        return attachments;\n    }\n\n    public long getVersion() {\n        return version;\n    }\n\n    public void updateVersion() {\n        this.version++;\n    }\n\n    public void setVersion(long version) {\n        this.version = version;\n    }\n\n    public Date getLastUpdateTime() {\n        return lastUpdateTime;\n    }\n\n    public void setLastUpdateTime(Date date) {\n        this.lastUpdateTime = date;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public void updateTime() {\n        this.lastUpdateTime = new Date();\n    }\n\n\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/TransactionManager.java",
    "content": "package io.tyloo;\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.common.TransactionType;\nimport org.apache.log4j.Logger;\n\nimport java.util.Deque;\nimport java.util.LinkedList;\nimport java.util.concurrent.ExecutorService;\n\n/*\n *\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:25 2019/6/7\n *\n */\n\npublic class TransactionManager {\n\n    static final Logger logger = Logger.getLogger(TransactionManager.class.getSimpleName());\n\n    private TransactionRepository transactionRepository;\n\n    /**\n     * ǰ߳\n     */\n    private static final ThreadLocal<Deque<Transaction>> CURRENT = new ThreadLocal<Deque<Transaction>>();\n\n    private ExecutorService executorService;\n\n    public void setTransactionRepository(TransactionRepository transactionRepository) {\n        this.transactionRepository = transactionRepository;\n    }\n\n    public void setExecutorService(ExecutorService executorService) {\n        this.executorService = executorService;\n    }\n\n    public TransactionManager() {\n\n\n    }\n\n    /**\n     *\n     * \n     *  begin ֮ʵҲʹһȫʵorder\n     * Żص rootMethodProceed ִ\n     *\n     * @param uniqueIdentify\n     * @return\n     */\n    public Transaction begin(Object uniqueIdentify) throws CloneNotSupportedException {\n        Transaction transaction = new Transaction(uniqueIdentify, TransactionType.ROOT);\n        transactionRepository.create(transaction);\n        registerTransaction(transaction);\n        return transaction;\n    }\n\n\n    /**\n     * ֧\n     *\n     * @param transactionContext \n     * @return ֧\n     */\n    public Transaction propagationNewBegin(TransactionContext transactionContext) throws CloneNotSupportedException {\n\n        Transaction transaction = new Transaction(transactionContext);\n        transactionRepository.create(transaction);\n\n        registerTransaction(transaction);\n        return transaction;\n    }\n\n    /**\n     * ȡ֧\n     *\n     * @param transactionContext \n     * @return ֧\n     * @throws NoExistedTransactionException 񲻴ʱ\n     */\n    public Transaction propagationExistBegin(TransactionContext transactionContext) throws NoExistedTransactionException {\n        Transaction transaction = null;\n        try {\n            transaction = transactionRepository.findByXid(transactionContext.getXid());\n        } catch (CloneNotSupportedException e) {\n            e.printStackTrace();\n        }\n\n        if (transaction != null) {\n            transaction.changeStatus(TransactionStatus.valueOf(transactionContext.getStatus()));\n            registerTransaction(transaction);\n            return transaction;\n        } else {\n            throw new NoExistedTransactionException();\n        }\n    }\n\n    public void commit(boolean asyncCommit) {\n\n        final Transaction transaction = getCurrentTransaction();\n\n        transaction.changeStatus(TransactionStatus.CONFIRMING);\n\n        transactionRepository.update(transaction);\n\n        if (asyncCommit) {\n            try {\n                Long statTime = System.currentTimeMillis();\n\n                executorService.submit(new Runnable() {\n                    @Override\n                    public void run() {\n                        commitTransaction(transaction);\n                    }\n                });\n                logger.debug(\"async submit cost time:\" + (System.currentTimeMillis() - statTime));\n            } catch (Throwable commitException) {\n                logger.warn(\"tyloo transaction async submit confirm failed, recovery job will try to confirm later.\", commitException);\n                throw new ConfirmingException(commitException);\n            }\n        } else {\n            commitTransaction(transaction);\n        }\n    }\n\n\n    public void rollback(boolean asyncRollback) {\n\n        final Transaction transaction = getCurrentTransaction();\n        transaction.changeStatus(TransactionStatus.CANCELLING);\n\n        transactionRepository.update(transaction);\n\n        if (asyncRollback) {\n\n            try {\n                executorService.submit(new Runnable() {\n                    @Override\n                    public void run() {\n                        rollbackTransaction(transaction);\n                    }\n                });\n            } catch (Throwable rollbackException) {\n                logger.warn(\"tyloo transaction async rollback failed, recovery job will try to rollback later.\", rollbackException);\n                throw new CancellingException(rollbackException);\n            }\n        } else {\n\n            rollbackTransaction(transaction);\n        }\n    }\n\n\n    private void commitTransaction(Transaction transaction) {\n        try {\n            transaction.commit();\n            transactionRepository.delete(transaction);\n        } catch (Throwable commitException) {\n            logger.warn(\"tyloo transaction confirm failed, recovery job will try to confirm later.\", commitException);\n            throw new ConfirmingException(commitException);\n        }\n    }\n\n    private void rollbackTransaction(Transaction transaction) {\n        try {\n            transaction.rollback();\n            transactionRepository.delete(transaction);\n        } catch (Throwable rollbackException) {\n            logger.warn(\"tyloo transaction rollback failed, recovery job will try to rollback later.\", rollbackException);\n            throw new CancellingException(rollbackException);\n        }\n    }\n\n    /**\n     * ȡǰ߳һ(ͷ)Ԫ\n     *\n     * @return \n     */\n    public Transaction getCurrentTransaction() {\n        if (isTransactionActive()) {\n            return CURRENT.get().peek();\n        }\n        return null;\n    }\n\n    public boolean isTransactionActive() {\n        Deque<Transaction> transactions = CURRENT.get();\n        return transactions != null && !transactions.isEmpty();\n    }\n\n    /**\n     * ע񵽵ǰ߳\n     *\n     * @param transaction \n     */\n    private void registerTransaction(Transaction transaction) {\n\n        if (CURRENT.get() == null) {\n            CURRENT.set(new LinkedList<Transaction>());\n        }\n\n        CURRENT.get().push(transaction);\n    }\n\n    /**\n     * ӵǰ߳Ƴ\n     *\n     * @param transaction \n     */\n    public void cleanAfterCompletion(Transaction transaction) {\n        if (isTransactionActive() && transaction != null) {\n            Transaction currentTransaction = getCurrentTransaction();\n            if (currentTransaction == transaction) {\n                CURRENT.get().pop();\n                if (CURRENT.get().size() == 0) {\n                    CURRENT.remove();\n                }\n            } else {\n                throw new SystemException(\"Illegal transaction when clean after completion\");\n            }\n        }\n    }\n\n    /**\n     * Ӳߵ\n     *\n     * @param participant \n     */\n    public void enlistParticipant(Participant participant) {\n        Transaction transaction = this.getCurrentTransaction();\n        transaction.enlistParticipant(participant);\n        transactionRepository.update(transaction);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/TransactionRepository.java",
    "content": "package io.tyloo;\n\nimport io.tyloo.api.TransactionXid;\n\nimport java.util.Date;\nimport java.util.List;\n\n/*\n *\n * (tccݿdao)\n * ʵڱĿdubbo-orderеconfig.spring.localappcontext-service-dao.xmlã\n * ʵSpringJdbcTransactionRepository̳JdbcTransactionRepositoryΪordercapitalredpacket3ķ\n * ÿһģжappcontext-service-dao.xmlӳ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:00 2019/6/12\n *\n */\n\npublic interface TransactionRepository {\n\n    int create(Transaction transaction) throws CloneNotSupportedException;\n\n    int update(Transaction transaction);\n\n    int delete(Transaction transaction);\n\n    Transaction findByXid(TransactionXid xid);\n\n    List<Transaction> findAllUnmodifiedSince(Date date);\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/common/MethodRole.java",
    "content": "package io.tyloo.common;\n\n/*\n * ɫ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:27 2019/4/15\n *\n */\npublic enum MethodRole {\n\n    /**\n     * \n     */\n    ROOT,\n\n    /**\n     * ṩ\n     */\n    PROVIDER,\n\n    /**\n     * \n     */\n    NORMAL\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/common/TransactionType.java",
    "content": "package io.tyloo.common;\n\n/*\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:28 2019/4/17\n *\n */\n\npublic enum TransactionType {\n\n    /**\n     * :1.\n     */\n    ROOT(1),\n\n    /**\n     * ֧:2.\n     */\n    BRANCH(2);\n\n    int id;\n\n    TransactionType(int id) {\n        this.id = id;\n    }\n\n    public static TransactionType valueOf(int id) {\n        switch (id) {\n            case 1:\n                return ROOT;\n            case 2:\n                return BRANCH;\n            default:\n                return null;\n        }\n    }\n\n    public int getId() {\n        return id;\n    }\n\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/context/MethodTransactionContextEditor.java",
    "content": "package io.tyloo.context;\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionContextEditor;\nimport io.tyloo.utils.TylooMethodUtils;\n\nimport java.lang.reflect.Method;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 16:42 2019/4/16\n *\n */\n\n@Deprecated\npublic class MethodTransactionContextEditor implements TransactionContextEditor {\n\n    @Override\n    public TransactionContext get(Object target, Method method, Object[] args) {\n        int position = TylooMethodUtils.getTransactionContextParamPosition(method.getParameterTypes());\n\n        if (position >= 0) {\n            return (TransactionContext) args[position];\n        }\n\n        return null;\n    }\n\n    @Override\n    public void set(TransactionContext transactionContext, Object target, Method method, Object[] args) {\n\n        int position = TylooMethodUtils.getTransactionContextParamPosition(method.getParameterTypes());\n        if (position >= 0) {\n            args[position] = transactionContext;\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/interceptor/TylooCoordinatorAspect.java",
    "content": "package io.tyloo.interceptor;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\n\n/*\n * ԴЭӦ\n * ͨ@Pointcut + @Around ע⣬ö @Tyloo עķ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 18:48 2019/4/22\n *\n */\n\n@Aspect\npublic abstract class TylooCoordinatorAspect {\n\n    private TylooCoordinatorInterceptor tylooCoordinatorInterceptor;\n\n    @Pointcut(\"@annotation(io.tyloo.api.Tyloo)\")\n    public void transactionContextCall() {\n\n    }\n\n    @Around(\"transactionContextCall()\")\n    public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {\n        return tylooCoordinatorInterceptor.interceptTransactionContextMethod(pjp);\n    }\n\n    public void setTylooCoordinatorInterceptor(TylooCoordinatorInterceptor tylooCoordinatorInterceptor) {\n        this.tylooCoordinatorInterceptor = tylooCoordinatorInterceptor;\n    }\n\n    public abstract int getOrder();\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/interceptor/TylooCoordinatorInterceptor.java",
    "content": "package io.tyloo.interceptor;\n\nimport io.tyloo.InvocationContext;\nimport io.tyloo.Participant;\nimport io.tyloo.Transaction;\nimport io.tyloo.TransactionManager;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.api.TransactionXid;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.support.FactoryBuilder;\nimport io.tyloo.utils.ReflectionUtils;\nimport io.tyloo.utils.TylooMethodUtils;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\n\nimport java.lang.reflect.Method;\n\n/*\n * ԴЭ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:35 2019/4/23\n *\n */\n\n\npublic class TylooCoordinatorInterceptor {\n\n    private TransactionManager transactionManager;\n\n    public void setTransactionManager(TransactionManager transactionManager) {\n        this.transactionManager = transactionManager;\n    }\n\n    public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {\n\n        Transaction transaction = transactionManager.getCurrentTransaction();\n        if (transaction != null) {\n            switch (transaction.getStatus()) {\n                case TRYING:\n                    enlistParticipant(pjp);\n                    break;\n                case CONFIRMING:\n                    break;\n                case CANCELLING:\n                    break;\n                default:\n                    throw new IllegalStateException(\"Unexpected value: \" + transaction.getStatus());\n            }\n        }\n\n        return pjp.proceed(pjp.getArgs());\n    }\n\n    /**\n     * ߣ Try ׶α\n     *\n     * @param pjp\n     * @throws IllegalAccessException\n     * @throws InstantiationException\n     */\n    private void enlistParticipant(ProceedingJoinPoint pjp) throws IllegalAccessException, InstantiationException, CloneNotSupportedException {\n\n        Method method = TylooMethodUtils.getTylooMethod(pjp);\n        if (method == null) {\n            throw new RuntimeException(String.format(\"join point not found method, point is : %s\", pjp.getSignature().getName()));\n        }\n        Tyloo tyloo = method.getAnnotation(Tyloo.class);\n\n        String confirmMethodName = tyloo.confirmMethod();\n        String cancelMethodName = tyloo.cancelMethod();\n\n        Transaction transaction = transactionManager.getCurrentTransaction();\n        TransactionXid xid = new TransactionXid(transaction.getXid().getGlobalTransactionId());\n\n        if (FactoryBuilder.factoryOf(tyloo.transactionContextEditor()).getInstance().get(pjp.getTarget(), method, pjp.getArgs()) == null) {\n            FactoryBuilder.factoryOf(tyloo.transactionContextEditor()).getInstance().set(new TransactionContext(xid, TransactionStatus.TRYING.getId()), pjp.getTarget(), ((MethodSignature) pjp.getSignature()).getMethod(), pjp.getArgs());\n        }\n\n        Class targetClass = ReflectionUtils.getDeclaringType(pjp.getTarget().getClass(), method.getName(), method.getParameterTypes());\n\n        InvocationContext confirmInvocation = new InvocationContext(targetClass,\n                confirmMethodName,\n                method.getParameterTypes(), pjp.getArgs());\n\n        InvocationContext cancelInvocation = new InvocationContext(targetClass,\n                cancelMethodName,\n                method.getParameterTypes(), pjp.getArgs());\n\n        Participant participant = new Participant(\n                xid,\n                confirmInvocation,\n                cancelInvocation,\n                tyloo.transactionContextEditor());\n        transactionManager.enlistParticipant(participant);\n\n    }\n\n\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/interceptor/TylooMethodContext.java",
    "content": "package io.tyloo.interceptor;\n\nimport io.tyloo.api.Propagation;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.UniqueIdentity;\nimport io.tyloo.common.MethodRole;\nimport io.tyloo.support.FactoryBuilder;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\n\n/*\n * עⷽ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:33 2019/4/24\n *\n */\npublic class TylooMethodContext {\n\n    /**\n     * \n     */\n    private ProceedingJoinPoint pjp = null;\n\n    /**\n     * עⷽ\n     */\n    private Method method = null;\n\n    /**\n     * ע\n     */\n    private Tyloo tyloo = null;\n\n    /**\n     * \n     */\n    private Propagation propagation = null;\n\n    /**\n     * \n     */\n    private TransactionContext transactionContext = null;\n\n    TylooMethodContext(ProceedingJoinPoint pjp) {\n        this.pjp = pjp;\n        this.method = getTylooMethod();\n        assert method != null;\n        this.tyloo = method.getAnnotation(Tyloo.class);\n        this.propagation = tyloo.propagation();\n        this.transactionContext = FactoryBuilder.factoryOf(tyloo.transactionContextEditor()).getInstance().get(pjp.getTarget(), method, pjp.getArgs());\n\n    }\n\n    public Tyloo getAnnotation() {\n        return tyloo;\n    }\n\n    public Propagation getPropagation() {\n        return propagation;\n    }\n\n    public TransactionContext getTransactionContext() {\n        return transactionContext;\n    }\n\n    public Method getMethod() {\n        return method;\n    }\n\n    /**\n     * ȡΨһʶ\n     *\n     * @return\n     */\n    public Object getUniqueIdentity() {\n        Annotation[][] annotations = this.getMethod().getParameterAnnotations();\n\n        for (int i = 0; i < annotations.length; i++) {\n            for (Annotation annotation : annotations[i]) {\n                if (annotation.annotationType().equals(UniqueIdentity.class)) {\n\n                    Object[] params = pjp.getArgs();\n\n                    return params[i];\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * ȡעⷽ\n     *\n     * @return\n     */\n    private Method getTylooMethod() {\n        Method method = ((MethodSignature) (pjp.getSignature())).getMethod();\n\n        if (method.getAnnotation(Tyloo.class) == null) {\n            try {\n                method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());\n            } catch (NoSuchMethodException e) {\n                return null;\n            }\n        }\n        return method;\n    }\n\n    /**\n     * ͨ÷񴫲ȡ\n     *\n     * @param isTransactionActive\n     * @return\n     */\n    public MethodRole getMethodRole(boolean isTransactionActive) {\n        if ((propagation.equals(Propagation.REQUIRED) && !isTransactionActive && transactionContext == null) ||\n                propagation.equals(Propagation.REQUIRES_NEW)) {\n            return MethodRole.ROOT;\n        } else if ((propagation.equals(Propagation.REQUIRED) || propagation.equals(Propagation.MANDATORY)) && !isTransactionActive && transactionContext != null) {\n            return MethodRole.PROVIDER;\n        } else {\n            return MethodRole.NORMAL;\n        }\n    }\n\n\n    public Object proceed() throws Throwable {\n        return this.pjp.proceed();\n    }\n}"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/interceptor/TylooTransactionAspect.java",
    "content": "package io.tyloo.interceptor;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\n\n/*\n * ɲӦ\n * ͨ@Pointcut + @Around ע⣬ö @Tyloo עķ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:14 2019/4/26\n *\n */\n@Aspect\npublic abstract class TylooTransactionAspect {\n\n    private TylooTransactionInterceptor tylooTransactionInterceptor;\n\n    public void setTylooTransactionInterceptor(TylooTransactionInterceptor tylooTransactionInterceptor) {\n        this.tylooTransactionInterceptor = tylooTransactionInterceptor;\n    }\n\n    @Pointcut(\"@annotation(io.tyloo.api.Tyloo)\")\n    public void tylooService() {\n\n    }\n\n    @Around(\"tylooService()\")\n    public Object interceptTylooMethod(ProceedingJoinPoint pjp) throws Throwable {\n\n        return tylooTransactionInterceptor.interceptTylooMethod(pjp);\n    }\n\n    public abstract int getOrder();\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/interceptor/TylooTransactionInterceptor.java",
    "content": "package io.tyloo.interceptor;\n\nimport com.alibaba.fastjson.JSON;\nimport io.tyloo.NoExistedTransactionException;\nimport io.tyloo.SystemException;\nimport io.tyloo.Transaction;\nimport io.tyloo.TransactionManager;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.utils.ReflectionUtils;\nimport io.tyloo.utils.TransactionUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.log4j.Logger;\nimport org.aspectj.lang.ProceedingJoinPoint;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/*\n * ɲ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:18 2019/4/23\n *\n */\npublic class TylooTransactionInterceptor {\n\n    static final Logger logger = Logger.getLogger(TylooTransactionInterceptor.class.getSimpleName());\n    private final Set<Class<? extends Exception>> delayCancelExceptions = new HashSet<>();\n    private TransactionManager transactionManager;\n\n    /**\n     * .\n     *\n     * @param transactionManager\n     */\n    public void setTransactionManager(TransactionManager transactionManager) {\n        this.transactionManager = transactionManager;\n    }\n\n    public void setDelayCancelExceptions(Set<Class<? extends Exception>> delayCancelExceptions) {\n        this.delayCancelExceptions.addAll(delayCancelExceptions);\n    }\n\n    /**\n     * ز.\n     *\n     * @param pjp\n     * @throws Throwable\n     */\n    public Object interceptTylooMethod(ProceedingJoinPoint pjp) throws Throwable {\n\n        TylooMethodContext tylooMethodContext = new TylooMethodContext(pjp);\n        boolean isTransactionActive = transactionManager.isTransactionActive();\n        if (!TransactionUtils.isLegalTransactionContext(isTransactionActive, tylooMethodContext)) {\n            throw new SystemException(\"no active tyloo transaction while propagation is mandatory for method \" + tylooMethodContext.getMethod().getName());\n        }\n\n        switch (tylooMethodContext.getMethodRole(isTransactionActive)) {\n            case ROOT:\n                return rootMethodProceed(tylooMethodContext);\n            case PROVIDER:\n                return providerMethodProceed(tylooMethodContext);\n            default:\n                return pjp.proceed();\n        }\n    }\n\n    /**\n     * 񷽷Ĵ.\n     *\n     * @param tylooMethodContext\n     * @throws Throwable\n     */\n    private Object rootMethodProceed(TylooMethodContext tylooMethodContext) throws Throwable {\n\n        Object returnValue = null;\n        Transaction transaction = null;\n\n        boolean asyncConfirm = tylooMethodContext.getAnnotation().asyncConfirm();\n        boolean asyncCancel = tylooMethodContext.getAnnotation().asyncCancel();\n\n        Set<Class<? extends Exception>> allDelayCancelExceptions = new HashSet<>();\n        allDelayCancelExceptions.addAll(this.delayCancelExceptions);\n        allDelayCancelExceptions.addAll(Arrays.asList(tylooMethodContext.getAnnotation().delayCancelExceptions()));\n\n        try {\n            transaction = transactionManager.begin(tylooMethodContext.getUniqueIdentity());\n            try {\n                returnValue = tylooMethodContext.proceed();\n            } catch (Throwable tryingException) {\n                if (!isDelayCancelException(tryingException, allDelayCancelExceptions)) {\n                    logger.warn(String.format(\"tyloo transaction trying failed. transaction content:%s\", JSON.toJSONString(transaction)), tryingException);\n                    transactionManager.rollback(asyncCancel);\n                }\n                throw tryingException;\n            }\n            transactionManager.commit(asyncConfirm);\n        } finally {\n            transactionManager.cleanAfterCompletion(transaction);\n        }\n        return returnValue;\n    }\n\n    /**\n     * ṩ񷽷.\n     * ״̬ CONFIRMING / CANCELLING öӦ\n     *\n     * @param tylooMethodContext\n     * @throws Throwable\n     */\n    private Object providerMethodProceed(TylooMethodContext tylooMethodContext) throws Throwable {\n\n        Transaction transaction = null;\n        boolean asyncConfirm = tylooMethodContext.getAnnotation().asyncConfirm();\n        boolean asyncCancel = tylooMethodContext.getAnnotation().asyncCancel();\n\n        try {\n            switch (TransactionStatus.valueOf(tylooMethodContext.getTransactionContext().getStatus())) {\n                case TRYING:\n                    transaction = transactionManager.propagationNewBegin(tylooMethodContext.getTransactionContext());\n                    return tylooMethodContext.proceed();\n                case CONFIRMING:\n                    try {\n                        transaction = transactionManager.propagationExistBegin(tylooMethodContext.getTransactionContext());\n                        transactionManager.commit(asyncConfirm);\n                    } catch (NoExistedTransactionException excepton) {\n                        //the transaction has been commit,ignore it.\n                    }\n                    break;\n                case CANCELLING:\n\n                    try {\n                        transaction = transactionManager.propagationExistBegin(tylooMethodContext.getTransactionContext());\n                        transactionManager.rollback(asyncCancel);\n                    } catch (NoExistedTransactionException exception) {\n                        //the transaction has been rollback,ignore it.\n                    }\n                    break;\n                default:\n                    throw new IllegalStateException(\"Unexpected value: \" + TransactionStatus.valueOf(tylooMethodContext.getTransactionContext().getStatus()));\n            }\n\n        } finally {\n            transactionManager.cleanAfterCompletion(transaction);\n        }\n        Method method = tylooMethodContext.getMethod();\n        return ReflectionUtils.getNullValue(method.getReturnType());\n    }\n\n    private boolean isDelayCancelException(Throwable throwable, Set<Class<? extends Exception>> delayCancelExceptions) {\n\n        if (delayCancelExceptions != null) {\n            for (Class delayCancelException : delayCancelExceptions) {\n                Throwable rootCause = ExceptionUtils.getRootCause(throwable);\n                if (!throwable.getClass().isAssignableFrom(delayCancelException)) {\n                    if ((rootCause != null) && rootCause.getClass().isAssignableFrom(delayCancelException)) {\n                        return true;\n                    }\n                } else {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/recover/TylooRecoverConfiguration.java",
    "content": "package io.tyloo.recover;\n\nimport java.util.Set;\n\n/*\n * ָýӿ.\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 18:50 2019/5/1\n *\n */\n\npublic interface TylooRecoverConfiguration {\n\n    /**\n     * ȡԴ\n     *\n     * @return\n     */\n    int getMaxRetryCount();\n\n    /**\n     * ȡҪִָĳʱ.\n     *\n     * @return\n     */\n    int getRecoverDuration();\n\n    /**\n     * ȡʱʽ.\n     *\n     * @return\n     */\n    String getCronExpression();\n\n    /**\n     * ӳȡ쳣\n     *\n     * @return\n     */\n    Set<Class<? extends Exception>> getDelayCancelExceptions();\n\n    void setDelayCancelExceptions(Set<Class<? extends Exception>> delayRecoverExceptions);\n\n    int getAsyncTerminateThreadCorePoolSize();\n\n    int getAsyncTerminateThreadMaxPoolSize();\n\n    int getAsyncTerminateThreadWorkQueueSize();\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/recover/TylooTransactionRecovery.java",
    "content": "package io.tyloo.recover;\n\nimport com.alibaba.fastjson.JSON;\nimport io.tyloo.OptimisticLockException;\nimport io.tyloo.Transaction;\nimport io.tyloo.TransactionRepository;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.common.TransactionType;\nimport io.tyloo.support.TransactionConfigurator;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.log4j.Logger;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\n\n/*\n *\n * ָ\n *\n * Ϣ־ûⲿĴ洢⣩С洢ָĻͨȡⲿ洢е쳣񣬶ʱᰴһƵʶԣֱɻ򳬹Դ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 18:51 2019/5/2\n *\n */\npublic class TylooTransactionRecovery {\n\n    static final Logger logger = Logger.getLogger(TylooTransactionRecovery.class.getSimpleName());\n\n    /**\n     * TCC.\n     */\n    private TransactionConfigurator transactionConfigurator;\n\n    /**\n     * ָ(RecoverScheduledJobʱ).\n     */\n    public void startRecover() throws CloneNotSupportedException {\n\n        List<Transaction> transactions = loadErrorTransactions();\n        recoverErrorTransactions(transactions);\n    }\n\n    /**\n     * ҳִдϢ\n     *\n     * @return\n     */\n    private List<Transaction> loadErrorTransactions() {\n        long currentTimeInMillis = Calendar.getInstance().getTimeInMillis();\n        TransactionRepository transactionRepository = transactionConfigurator.getTransactionRepository();\n        TylooRecoverConfiguration tylooRecoverConfiguration = transactionConfigurator.getTylooRecoverConfiguration();\n        return transactionRepository.findAllUnmodifiedSince(new Date(currentTimeInMillis - tylooRecoverConfiguration.getRecoverDuration() * 1000));\n    }\n\n    /**\n     * ָ.\n     *\n     * @param transactions\n     */\n    private void recoverErrorTransactions(List<Transaction> transactions) throws CloneNotSupportedException {\n\n        for (Transaction transaction : transactions) {\n            //ȽԴ\n            if (transaction.getRetriedCount() > transactionConfigurator.getTylooRecoverConfiguration().getMaxRetryCount()) {\n                logger.error(String.format(\"recover failed with max retry count,will not try again. txid:%s, status:%s,retried count:%d,transaction content:%s\", transaction.getXid(), transaction.getStatus().getId(), transaction.getRetriedCount(), JSON.toJSONString(transaction)));\n                continue;\n            }\n\n            //ǰǷ֧ʱ\n            if (transaction.getTransactionType().equals(TransactionType.BRANCH)\n                    && (transaction.getCreateTime().getTime() +\n                    transactionConfigurator.getTylooRecoverConfiguration().getMaxRetryCount() *\n                            transactionConfigurator.getTylooRecoverConfiguration().getRecoverDuration() * 1000\n                    > System.currentTimeMillis())) {\n                continue;\n            }\n\n            try {\n                transaction.addRetriedCount();\n                // CONFIRMING(2)״̬ǰִ\n                if (transaction.getStatus().equals(TransactionStatus.CONFIRMING)) {\n                    transaction.changeStatus(TransactionStatus.CONFIRMING);\n                    transactionConfigurator.getTransactionRepository().update(transaction);\n                    transaction.commit();\n                    transactionConfigurator.getTransactionRepository().delete(transaction);\n\n                } else if (transaction.getStatus().equals(TransactionStatus.CANCELLING)\n                        || transaction.getTransactionType().equals(TransactionType.ROOT)) {\n                    // ״̬ΪCANCELLING(3)Ȼִлع\n                    transaction.changeStatus(TransactionStatus.CANCELLING);\n                    transactionConfigurator.getTransactionRepository().update(transaction);\n                    transaction.rollback();\n                    // £ʱû־ֱɾ\n                    transactionConfigurator.getTransactionRepository().delete(transaction);\n                }\n\n            } catch (Throwable throwable) {\n                if (throwable instanceof OptimisticLockException\n                        || ExceptionUtils.getRootCause(throwable) instanceof OptimisticLockException) {\n                    logger.warn(String.format(\"optimisticLockException happened while recover. txid:%s, status:%s,retried count:%d,transaction content:%s\", transaction.getXid(), transaction.getStatus().getId(), transaction.getRetriedCount(), JSON.toJSONString(transaction)), throwable);\n                } else {\n                    logger.error(String.format(\"recover failed, txid:%s, status:%s,retried count:%d,transaction content:%s\", transaction.getXid(), transaction.getStatus().getId(), transaction.getRetriedCount(), JSON.toJSONString(transaction)), throwable);\n                }\n            }\n        }\n    }\n\n    public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {\n        this.transactionConfigurator = transactionConfigurator;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/CachableTransactionRepository.java",
    "content": "package io.tyloo.repository;\n\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport io.tyloo.ConcurrentTransactionException;\nimport io.tyloo.OptimisticLockException;\nimport io.tyloo.Transaction;\nimport io.tyloo.TransactionRepository;\nimport io.tyloo.api.TransactionXid;\n\nimport javax.transaction.xa.Xid;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/*\n *\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:08 2019/5/12\n *\n */\npublic abstract class CachableTransactionRepository implements TransactionRepository {\n\n    /**\n     * ʱ(Ϊλ)\n     */\n    private int expireDuration = 120;\n    /**\n     * ־¼<Xid, Transaction>\n     */\n    private final Cache<Xid, Transaction> transactionXidTylooTransactionCache;\n\n    public CachableTransactionRepository() {\n        transactionXidTylooTransactionCache = CacheBuilder.newBuilder().expireAfterAccess(expireDuration, TimeUnit.SECONDS).maximumSize(1000).build();\n    }\n\n    /**\n     * ־¼\n     */\n    @Override\n    public int create(Transaction transaction) throws CloneNotSupportedException {\n        int result = doCreate(transaction);\n        if (result > 0) {\n            putToCache(transaction);\n        } else {\n            throw new ConcurrentTransactionException(\"transaction xid duplicated. xid:\" + transaction.getXid().toString());\n        }\n\n        return result;\n    }\n\n    /**\n     * ־¼\n     */\n    @Override\n    public int update(Transaction transaction) {\n        int result = 0;\n\n        try {\n            result = doUpdate(transaction);\n            if (result > 0) {\n                putToCache(transaction);\n            } else {\n                throw new OptimisticLockException();\n            }\n        } catch (CloneNotSupportedException e) {\n            e.printStackTrace();\n        } finally {\n            if (result <= 0) {\n                removeFromCache(transaction);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * ɾ־¼\n     */\n    @Override\n    public int delete(Transaction transaction) {\n        int result = 0;\n\n        try {\n            result = doDelete(transaction);\n\n        } catch (CloneNotSupportedException e) {\n            e.printStackTrace();\n        } finally {\n            removeFromCache(transaction);\n        }\n        return result;\n    }\n\n    /**\n     * xid־¼.\n     *\n     * @param transactionXid\n     * @return\n     */\n    @Override\n    public Transaction findByXid(TransactionXid transactionXid) {\n        Transaction transaction = findFromCache(transactionXid);\n\n        if (transaction == null) {\n            transaction = doFindOne(transactionXid);\n            if (transaction != null) {\n                putToCache(transaction);\n            }\n        }\n\n        return transaction;\n    }\n\n    /**\n     * ҳδ־ĳһʱ㿪ʼ.\n     *\n     * @return\n     */\n    @Override\n    public List<Transaction> findAllUnmodifiedSince(Date date) {\n\n        List<Transaction> transactions = doFindAllUnmodifiedSince(date);\n        for (Transaction transaction : transactions) {\n            putToCache(transaction);\n        }\n        return transactions;\n    }\n\n    /**\n     * 뻺.\n     *\n     * @param transaction\n     */\n    protected void putToCache(Transaction transaction) {\n        transactionXidTylooTransactionCache.put(transaction.getXid(), transaction);\n    }\n\n    /**\n     * ӻɾ.\n     *\n     * @param transaction\n     */\n    protected void removeFromCache(Transaction transaction) {\n        transactionXidTylooTransactionCache.invalidate(transaction.getXid());\n    }\n\n    /**\n     * ӻв.\n     *\n     * @param transactionXid\n     * @return\n     */\n    protected Transaction findFromCache(TransactionXid transactionXid) {\n        return transactionXidTylooTransactionCache.getIfPresent(transactionXid);\n    }\n\n    public void setExpireDuration(int durationInSeconds) {\n        this.expireDuration = durationInSeconds;\n    }\n\n    /**\n     * ־¼\n     *\n     * @param transaction\n     * @return\n     */\n    protected abstract int doCreate(Transaction transaction) throws CloneNotSupportedException;\n\n    protected abstract int doUpdate(Transaction transaction) throws CloneNotSupportedException;\n\n    protected abstract int doDelete(Transaction transaction) throws CloneNotSupportedException;\n\n    protected abstract Transaction doFindOne(Xid xid);\n\n    protected abstract List<Transaction> doFindAllUnmodifiedSince(Date date);\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/FileSystemTransactionRepository.java",
    "content": "package io.tyloo.repository;\n\nimport io.tyloo.Transaction;\nimport io.tyloo.repository.helper.TransactionSerializer;\nimport io.tyloo.serializer.KryoPoolSerializer;\nimport io.tyloo.serializer.ObjectSerializer;\n\nimport javax.transaction.xa.Xid;\nimport java.io.*;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/*\n * ļϵͳ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:22 2019/5/14\n *\n */\npublic class FileSystemTransactionRepository extends CachableTransactionRepository {\n\n    private String rootPath = \"/tcc\";\n\n    private volatile boolean initialized;\n\n    private ObjectSerializer serializer = new KryoPoolSerializer();\n\n    public void setSerializer(ObjectSerializer serializer) {\n        this.serializer = serializer;\n    }\n\n    public void setRootPath(String rootPath) {\n        this.rootPath = rootPath;\n    }\n\n    @Override\n    protected int doCreate(Transaction transaction) throws CloneNotSupportedException {\n        return createFile(transaction);\n    }\n\n    @Override\n    protected int doUpdate(Transaction transaction) throws CloneNotSupportedException {\n\n        transaction.updateVersion();\n        transaction.updateTime();\n\n        writeFile(transaction);\n        return 1;\n    }\n\n    @Override\n    protected int doDelete(Transaction transaction) throws CloneNotSupportedException {\n\n        String fullFileName = getFullFileName(transaction.getXid());\n        File file = new File(fullFileName);\n        if (file.exists()) {\n            return file.delete() ? 1 : 0;\n        }\n        return 1;\n    }\n\n    @Override\n    protected Transaction doFindOne(Xid xid) {\n\n        String fullFileName = getFullFileName(xid);\n        File file = new File(fullFileName);\n\n        if (file.exists()) {\n            return readTransaction(file);\n        }\n\n        return null;\n    }\n\n    @Override\n    protected List<Transaction> doFindAllUnmodifiedSince(Date date) {\n\n        List<Transaction> allTransactions = doFindAll();\n\n        List<Transaction> allUnmodifiedSince = new ArrayList<Transaction>();\n\n        for (Transaction transaction : allTransactions) {\n            if (transaction.getLastUpdateTime().compareTo(date) < 0) {\n                allUnmodifiedSince.add(transaction);\n            }\n        }\n\n        return allUnmodifiedSince;\n    }\n\n\n    protected List<Transaction> doFindAll() {\n\n        List<Transaction> transactions = new ArrayList<Transaction>();\n        File path = new File(rootPath);\n        File[] files = path.listFiles();\n\n        for (File file : files) {\n            Transaction transaction = readTransaction(file);\n            transactions.add(transaction);\n        }\n\n        return transactions;\n    }\n\n    private String getFullFileName(Xid xid) {\n        return String.format(\"%s/%s\", rootPath, xid);\n    }\n\n    private void makeDirIfNecessary() {\n        if (!initialized) {\n            synchronized (FileSystemTransactionRepository.class) {\n                if (!initialized) {\n                    File rootPathFile = new File(rootPath);\n                    if (!rootPathFile.exists()) {\n\n                        boolean result = rootPathFile.mkdir();\n\n                        if (!result) {\n                            throw new TransactionIOException(\"cannot create root path, the path to create is:\" + rootPath);\n                        }\n\n                        initialized = true;\n                    } else if (!rootPathFile.isDirectory()) {\n                        throw new TransactionIOException(\"rootPath is not directory\");\n                    }\n                }\n            }\n        }\n    }\n\n\n    private int createFile(Transaction transaction) throws CloneNotSupportedException {\n        makeDirIfNecessary();\n\n        String filePath = getFullFileName(transaction.getXid());\n\n        FileChannel channel = null;\n        RandomAccessFile raf = null;\n        File file = null;\n\n        byte[] content = TransactionSerializer.serialize(serializer, transaction);\n\n        try {\n\n            file = new File(filePath);\n\n            boolean result = file.createNewFile();\n\n            if (!result) {\n                return 0;\n            }\n\n            raf = new RandomAccessFile(file, \"rw\");\n            channel = raf.getChannel();\n            ByteBuffer buffer = ByteBuffer.allocate(content.length);\n            buffer.put(content);\n            buffer.flip();\n\n            while (buffer.hasRemaining()) {\n                channel.write(buffer);\n            }\n\n            channel.force(true);\n\n            return 1;\n\n        } catch (FileNotFoundException e) {\n            throw new TransactionIOException(e);\n        } catch (IOException e) {\n            throw new TransactionIOException(e);\n        } finally {\n            if (channel != null && channel.isOpen()) {\n                try {\n                    channel.close();\n                } catch (IOException e) {\n                    throw new TransactionIOException(e);\n                }\n            }\n        }\n    }\n\n    private void writeFile(Transaction transaction) throws CloneNotSupportedException {\n\n        makeDirIfNecessary();\n\n        String filePath = getFullFileName(transaction.getXid());\n\n        FileChannel channel = null;\n        RandomAccessFile raf = null;\n\n        byte[] content = TransactionSerializer.serialize(serializer, transaction);\n\n        try {\n\n            raf = new RandomAccessFile(filePath, \"rw\");\n            channel = raf.getChannel();\n            ByteBuffer buffer = ByteBuffer.allocate(content.length);\n            buffer.put(content);\n            buffer.flip();\n\n            while (buffer.hasRemaining()) {\n                channel.write(buffer);\n            }\n\n            channel.force(true);\n\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        } finally {\n            if (channel != null && channel.isOpen()) {\n                try {\n                    channel.close();\n                } catch (IOException e) {\n                    throw new TransactionIOException(e);\n                }\n            }\n        }\n    }\n\n    private Transaction readTransaction(File file) {\n\n        FileInputStream fis = null;\n        try {\n            fis = new FileInputStream(file);\n\n            byte[] content = new byte[(int) file.length()];\n\n            fis.read(content);\n\n            if (content != null) {\n                return TransactionSerializer.deserialize(serializer, content);\n            }\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        } finally {\n            if (fis != null) {\n                try {\n                    fis.close();\n                } catch (IOException e) {\n                    throw new TransactionIOException(e);\n                }\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/JdbcTransactionRepository.java",
    "content": "package io.tyloo.repository;\n\n\nimport io.tyloo.Transaction;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.serializer.KryoPoolSerializer;\nimport io.tyloo.serializer.ObjectSerializer;\nimport io.tyloo.utils.CollectionUtils;\nimport io.tyloo.utils.StringUtils;\n\nimport javax.sql.DataSource;\nimport javax.transaction.xa.Xid;\nimport java.sql.*;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/*\n *\n * JDBC⣨Ӧ÷ʵעԴ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:26 2019/5/13\n *\n */\npublic class JdbcTransactionRepository extends CachableTransactionRepository {\n\n    private String domain;\n\n    private String tbSuffix;\n\n    private DataSource dataSource;\n\n    private ObjectSerializer<Transaction> serializer = new KryoPoolSerializer();\n\n    public String getDomain() {\n        return domain;\n    }\n\n    public void setDomain(String domain) {\n        this.domain = domain;\n    }\n\n    public String getTbSuffix() {\n        return tbSuffix;\n    }\n\n    public void setTbSuffix(String tbSuffix) {\n        this.tbSuffix = tbSuffix;\n    }\n\n    public void setSerializer(ObjectSerializer<Transaction> serializer) {\n        this.serializer = serializer;\n    }\n\n    public DataSource getDataSource() {\n        return dataSource;\n    }\n\n    public void setDataSource(DataSource dataSource) {\n        this.dataSource = dataSource;\n    }\n\n    @Override\n    protected int doCreate(Transaction transaction) {\n\n        Connection connection = null;\n        PreparedStatement stmt = null;\n\n        try {\n            connection = this.getConnection();\n\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"INSERT INTO \" + getTableName() +\n                    \"(GLOBAL_TX_ID,BRANCH_QUALIFIER,TRANSACTION_TYPE,CONTENT,STATUS,RETRIED_COUNT,CREATE_TIME,LAST_UPDATE_TIME,VERSION\");\n            builder.append(StringUtils.isNotEmpty(domain) ? \",DOMAIN ) VALUES (?,?,?,?,?,?,?,?,?,?)\" : \") VALUES (?,?,?,?,?,?,?,?,?)\");\n\n            stmt = connection.prepareStatement(builder.toString());\n\n            stmt.setBytes(1, transaction.getXid().getGlobalTransactionId());\n            stmt.setBytes(2, transaction.getXid().getBranchQualifier());\n            stmt.setInt(3, transaction.getTransactionType().getId());\n            stmt.setBytes(4, serializer.serialize(transaction));\n            stmt.setInt(5, transaction.getStatus().getId());\n            stmt.setInt(6, transaction.getRetriedCount());\n            stmt.setTimestamp(7, new java.sql.Timestamp(transaction.getCreateTime().getTime()));\n            stmt.setTimestamp(8, new java.sql.Timestamp(transaction.getLastUpdateTime().getTime()));\n            stmt.setLong(9, transaction.getVersion());\n\n            if (StringUtils.isNotEmpty(domain)) {\n                stmt.setString(10, domain);\n            }\n\n            stmt.executeUpdate();\n            return 1;\n        } catch (SQLException e) {\n            if (e instanceof SQLIntegrityConstraintViolationException) {\n                return 0;\n            } else {\n                throw new TransactionIOException(e);\n            }\n        } catch (Throwable throwable) {\n            throw new TransactionIOException(throwable);\n        } finally {\n            closeStatement(stmt);\n            this.releaseConnection(connection);\n        }\n    }\n\n    @Override\n    protected int doUpdate(Transaction transaction) {\n        Connection connection = null;\n        PreparedStatement stmt = null;\n\n        java.util.Date lastUpdateTime = transaction.getLastUpdateTime();\n        long currentVersion = transaction.getVersion();\n\n        transaction.updateTime();\n        transaction.updateVersion();\n\n        try {\n            connection = this.getConnection();\n\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"UPDATE \" + getTableName() + \" SET \" +\n                    \"CONTENT = ?,STATUS = ?,LAST_UPDATE_TIME = ?, RETRIED_COUNT = ?,VERSION = VERSION+1 WHERE GLOBAL_TX_ID = ? AND BRANCH_QUALIFIER = ? AND VERSION = ?\");\n\n            builder.append(StringUtils.isNotEmpty(domain) ? \" AND DOMAIN = ?\" : \"\");\n\n            stmt = connection.prepareStatement(builder.toString());\n\n            stmt.setBytes(1, serializer.serialize(transaction));\n            stmt.setInt(2, transaction.getStatus().getId());\n            stmt.setTimestamp(3, new Timestamp(transaction.getLastUpdateTime().getTime()));\n\n            stmt.setInt(4, transaction.getRetriedCount());\n            stmt.setBytes(5, transaction.getXid().getGlobalTransactionId());\n            stmt.setBytes(6, transaction.getXid().getBranchQualifier());\n            stmt.setLong(7, currentVersion);\n\n            if (StringUtils.isNotEmpty(domain)) {\n                stmt.setString(8, domain);\n            }\n\n            int result = stmt.executeUpdate();\n\n            return result;\n\n        } catch (Throwable e) {\n            transaction.setLastUpdateTime(lastUpdateTime);\n            transaction.setVersion(currentVersion);\n            throw new TransactionIOException(e);\n        } finally {\n            closeStatement(stmt);\n            this.releaseConnection(connection);\n        }\n    }\n\n    @Override\n    protected int doDelete(Transaction transaction) {\n        Connection connection = null;\n        PreparedStatement stmt = null;\n\n        try {\n            connection = this.getConnection();\n            String builder = \"DELETE FROM \" + getTableName() +\n                    \" WHERE GLOBAL_TX_ID = ? AND BRANCH_QUALIFIER = ?\" +\n                    (StringUtils.isNotEmpty(domain) ? \" AND DOMAIN = ?\" : \"\");\n            stmt = connection.prepareStatement(builder);\n\n            stmt.setBytes(1, transaction.getXid().getGlobalTransactionId());\n            stmt.setBytes(2, transaction.getXid().getBranchQualifier());\n\n            if (StringUtils.isNotEmpty(domain)) {\n                stmt.setString(3, domain);\n            }\n            return stmt.executeUpdate();\n\n        } catch (SQLException e) {\n            throw new TransactionIOException(e);\n        } finally {\n            closeStatement(stmt);\n            this.releaseConnection(connection);\n        }\n    }\n\n    @Override\n    protected Transaction doFindOne(Xid xid) {\n\n        List<Transaction> transactions = doFind(Arrays.asList(xid));\n\n        if (!CollectionUtils.isEmpty(transactions)) {\n            return transactions.get(0);\n        }\n        return null;\n    }\n\n    @Override\n    protected List<Transaction> doFindAllUnmodifiedSince(java.util.Date date) {\n\n        List<Transaction> transactions = new ArrayList<Transaction>();\n\n        Connection connection = null;\n        PreparedStatement stmt = null;\n\n        try {\n            connection = this.getConnection();\n\n            StringBuilder builder = new StringBuilder();\n\n            builder.append(\"SELECT GLOBAL_TX_ID, BRANCH_QUALIFIER, CONTENT,STATUS,TRANSACTION_TYPE,CREATE_TIME,LAST_UPDATE_TIME,RETRIED_COUNT,VERSION\");\n            builder.append(StringUtils.isNotEmpty(domain) ? \",DOMAIN\" : \"\");\n            builder.append(\"  FROM \" + getTableName() + \" WHERE LAST_UPDATE_TIME < ?\");\n            builder.append(\" AND IS_DELETE = 0 \");\n            builder.append(StringUtils.isNotEmpty(domain) ? \" AND DOMAIN = ?\" : \"\");\n\n            stmt = connection.prepareStatement(builder.toString());\n\n            stmt.setTimestamp(1, new Timestamp(date.getTime()));\n\n            if (StringUtils.isNotEmpty(domain)) {\n                stmt.setString(2, domain);\n            }\n\n            ResultSet resultSet = stmt.executeQuery();\n\n            this.constructTransactions(resultSet, transactions);\n        } catch (Throwable e) {\n            throw new TransactionIOException(e);\n        } finally {\n            closeStatement(stmt);\n            this.releaseConnection(connection);\n        }\n\n        return transactions;\n    }\n\n    protected List<Transaction> doFind(List<Xid> xids) {\n\n        List<Transaction> transactions = new ArrayList<Transaction>();\n        if (CollectionUtils.isEmpty(xids)) {\n            return transactions;\n        }\n\n        Connection connection = null;\n        PreparedStatement stmt = null;\n\n        try {\n            connection = this.getConnection();\n\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"SELECT GLOBAL_TX_ID, BRANCH_QUALIFIER, CONTENT,STATUS,TRANSACTION_TYPE,CREATE_TIME,LAST_UPDATE_TIME,RETRIED_COUNT,VERSION\");\n            builder.append(StringUtils.isNotEmpty(domain) ? \",DOMAIN\" : \"\");\n            builder.append(\"  FROM \" + getTableName() + \" WHERE\");\n\n            if (!CollectionUtils.isEmpty(xids)) {\n                for (Xid xid : xids) {\n                    builder.append(\" ( GLOBAL_TX_ID = ? AND BRANCH_QUALIFIER = ? ) OR\");\n                }\n\n                builder.delete(builder.length() - 2, builder.length());\n            }\n\n            builder.append(StringUtils.isNotEmpty(domain) ? \" AND DOMAIN = ?\" : \"\");\n\n            stmt = connection.prepareStatement(builder.toString());\n\n            int i = 0;\n\n            for (Xid xid : xids) {\n                stmt.setBytes(++i, xid.getGlobalTransactionId());\n                stmt.setBytes(++i, xid.getBranchQualifier());\n            }\n\n            if (StringUtils.isNotEmpty(domain)) {\n                stmt.setString(++i, domain);\n            }\n\n            ResultSet resultSet = stmt.executeQuery();\n\n            this.constructTransactions(resultSet, transactions);\n        } catch (Throwable e) {\n            throw new TransactionIOException(e);\n        } finally {\n            closeStatement(stmt);\n            this.releaseConnection(connection);\n        }\n\n        return transactions;\n    }\n\n    protected void constructTransactions(ResultSet resultSet, List<Transaction> transactions) throws SQLException {\n        while (resultSet.next()) {\n            byte[] transactionBytes = resultSet.getBytes(3);\n            Transaction transaction = serializer.deserialize(transactionBytes);\n            transaction.changeStatus(TransactionStatus.valueOf(resultSet.getInt(4)));\n            transaction.setLastUpdateTime(resultSet.getDate(7));\n            transaction.setVersion(resultSet.getLong(9));\n            transaction.resetRetriedCount(resultSet.getInt(8));\n            transactions.add(transaction);\n        }\n    }\n\n\n    protected Connection getConnection() {\n        try {\n            return this.dataSource.getConnection();\n        } catch (SQLException e) {\n            throw new TransactionIOException(e);\n        }\n    }\n\n    protected void releaseConnection(Connection con) {\n        try {\n            if (con != null && !con.isClosed()) {\n                con.close();\n            }\n        } catch (SQLException e) {\n            throw new TransactionIOException(e);\n        }\n    }\n\n    private void closeStatement(Statement stmt) {\n        try {\n            if (stmt != null && !stmt.isClosed()) {\n                stmt.close();\n            }\n        } catch (Exception ex) {\n            throw new TransactionIOException(ex);\n        }\n    }\n\n    private String getTableName() {\n        return StringUtils.isNotEmpty(tbSuffix) ? \"TCC_TRANSACTION\" + tbSuffix : \"TCC_TRANSACTION\";\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/RedisTransactionRepository.java",
    "content": "package io.tyloo.repository;\n\nimport io.tyloo.Transaction;\nimport io.tyloo.repository.helper.ExpandTransactionSerializer;\nimport io.tyloo.repository.helper.JedisCallback;\nimport io.tyloo.repository.helper.RedisHelper;\nimport io.tyloo.serializer.KryoPoolSerializer;\nimport io.tyloo.serializer.ObjectSerializer;\nimport org.apache.log4j.Logger;\nimport redis.clients.jedis.*;\n\nimport javax.transaction.xa.Xid;\nimport java.util.*;\n\n/*\n *\n * Redis\n * JedisRedisٷƼJavaӿ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:27 2019/5/17\n *\n */\npublic class RedisTransactionRepository extends CachableTransactionRepository {\n\n    private static final Logger logger = Logger.getLogger(RedisTransactionRepository.class.getSimpleName());\n\n    private JedisPool jedisPool;\n\n    private String keyPrefix = \"TCC:\";\n\n    private int fetchKeySize = 1000;\n\n    private boolean isSupportScan = true;\n\n    private boolean isForbiddenKeys = false;\n    private ObjectSerializer serializer = new KryoPoolSerializer();\n\n    public void setKeyPrefix(String keyPrefix) {\n        this.keyPrefix = keyPrefix;\n    }\n\n    public void setSerializer(ObjectSerializer serializer) {\n        this.serializer = serializer;\n    }\n\n    public int getFetchKeySize() {\n        return fetchKeySize;\n    }\n\n    public void setFetchKeySize(int fetchKeySize) {\n        this.fetchKeySize = fetchKeySize;\n    }\n\n    public JedisPool getJedisPool() {\n        return jedisPool;\n    }\n\n    public void setJedisPool(JedisPool jedisPool) {\n\n        this.jedisPool = jedisPool;\n\n        isSupportScan = RedisHelper.isSupportScanCommand(jedisPool.getResource());\n\n        if (!isSupportScan && isForbiddenKeys) {\n            throw new RuntimeException(\"Redis not support 'scan' command, \" +\n                    \"and 'keys' command is forbidden, \" +\n                    \"try update redis version higher than 2.8.0 \" +\n                    \"or set 'isForbiddenKeys' to false\");\n        }\n    }\n\n    public void setSupportScan(boolean isSupportScan) {\n        this.isSupportScan = isSupportScan;\n    }\n\n    public void setForbiddenKeys(boolean forbiddenKeys) {\n        isForbiddenKeys = forbiddenKeys;\n    }\n\n    @Override\n    protected int doCreate(final Transaction transaction) {\n\n        try {\n            Long statusCode = RedisHelper.execute(jedisPool, jedis -> {\n\n                List<byte[]> params = new ArrayList<>();\n\n                for (Map.Entry<byte[], byte[]> entry : ExpandTransactionSerializer.serialize(serializer, transaction).entrySet()) {\n                    params.add(entry.getKey());\n                    params.add(entry.getValue());\n                }\n\n                Object result = jedis.eval(\"if redis.call('exists', KEYS[1]) == 0 then redis.call('hmset', KEYS[1], unpack(ARGV)); return 1; end; return 0;\".getBytes(),\n                        Arrays.asList(RedisHelper.getRedisKey(keyPrefix, transaction.getXid())), params);\n\n                return (Long) result;\n            });\n            return statusCode.intValue();\n\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        }\n    }\n\n    @Override\n    protected int doUpdate(final Transaction transaction) {\n\n        try {\n            Long statusCode = RedisHelper.execute(jedisPool, jedis -> {\n\n                transaction.updateTime();\n                transaction.updateVersion();\n\n                List<byte[]> params = new ArrayList<>();\n\n                for (Map.Entry<byte[], byte[]> entry : ExpandTransactionSerializer.serialize(serializer, transaction).entrySet()) {\n                    params.add(entry.getKey());\n                    params.add(entry.getValue());\n                }\n\n                Object result = jedis.eval(String.format(\"if redis.call('hget',KEYS[1],'VERSION') == '%s' then redis.call('hmset', KEYS[1], unpack(ARGV)); return 1; end; return 0;\",\n                        transaction.getVersion() - 1).getBytes(),\n                        Arrays.asList(RedisHelper.getRedisKey(keyPrefix, transaction.getXid())), params);\n\n                return (Long) result;\n            });\n\n            return statusCode.intValue();\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        }\n    }\n\n    @Override\n    protected int doDelete(final Transaction transaction) {\n        try {\n\n            Long result = RedisHelper.execute(jedisPool, jedis -> jedis.del(RedisHelper.getRedisKey(keyPrefix, transaction.getXid())));\n\n            return result.intValue();\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        }\n    }\n\n    @Override\n    protected Transaction doFindOne(final Xid xid) {\n\n        try {\n            Long startTime = System.currentTimeMillis();\n            Map<byte[], byte[]> content = RedisHelper.execute(jedisPool, jedis -> jedis.hgetAll(RedisHelper.getRedisKey(keyPrefix, xid)));\n            logger.info(\"redis find cost time :\" + (System.currentTimeMillis() - startTime));\n\n            if (content != null && content.size() > 0) {\n                return ExpandTransactionSerializer.deserialize(serializer, content);\n            }\n            return null;\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        }\n    }\n\n    @Override\n    protected List<Transaction> doFindAllUnmodifiedSince(Date date) {\n\n        List<Transaction> allTransactions = doFindAll();\n        List<Transaction> allUnmodifiedSince = new ArrayList<>();\n\n        for (Transaction transaction : allTransactions) {\n            if (transaction.getLastUpdateTime().compareTo(date) < 0) {\n                allUnmodifiedSince.add(transaction);\n            }\n        }\n\n        return allUnmodifiedSince;\n    }\n\n    //    @Override\n    protected List<Transaction> doFindAll() {\n\n        try {\n            final Set<byte[]> keys = RedisHelper.execute(jedisPool, jedis -> {\n                if (isSupportScan) {\n                    List<String> allKeys = new ArrayList<>();\n                    String cursor = RedisHelper.SCAN_INIT_CURSOR;\n                    ScanParams scanParams = RedisHelper.buildDefaultScanParams(keyPrefix + \"*\", fetchKeySize);\n                    do {\n                        ScanResult<String> scanResult = jedis.scan(cursor, scanParams);\n                        allKeys.addAll(scanResult.getResult());\n                        cursor = scanResult.getStringCursor();\n                    } while (!cursor.equals(RedisHelper.SCAN_INIT_CURSOR));\n\n                    Set<byte[]> allKeySet = new HashSet<>();\n\n                    for (String key : allKeys) {\n                        allKeySet.add(key.getBytes());\n                    }\n                    logger.info(String.format(\"find all key by scan command with pattern:%s allKeySet.size()=%d\", keyPrefix + \"*\", allKeySet.size()));\n                    return allKeySet;\n                } else {\n                    return jedis.keys((keyPrefix + \"*\").getBytes());\n                }\n\n            });\n\n\n            return RedisHelper.execute(jedisPool, jedis -> {\n\n                Pipeline pipeline = jedis.pipelined();\n\n                for (final byte[] key : keys) {\n                    pipeline.hgetAll(key);\n                }\n                List<Object> result = pipeline.syncAndReturnAll();\n\n                List<Transaction> list = new ArrayList<>();\n                for (Object data : result) {\n                    if (data != null && ((Map<byte[], byte[]>) data).size() > 0) {\n                        list.add(ExpandTransactionSerializer.deserialize(serializer, (Map<byte[], byte[]>) data));\n                    }\n                }\n                return list;\n            });\n\n        } catch (Exception e) {\n            throw new TransactionIOException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/TransactionIOException.java",
    "content": "package io.tyloo.repository;\n\n/*\n *\n * IO쳣\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:27 2019/5/16\n *\n */\npublic class TransactionIOException extends RuntimeException {\n\n    private static final long serialVersionUID = 6508064607297986329L;\n\n    public TransactionIOException(String message) {\n        super(message);\n    }\n\n    public TransactionIOException(Throwable e) {\n        super(e);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/helper/ExpandTransactionSerializer.java",
    "content": "package io.tyloo.repository.helper;\n\nimport com.alibaba.fastjson.JSON;\nimport io.tyloo.SystemException;\nimport io.tyloo.Transaction;\nimport io.tyloo.api.TransactionStatus;\nimport io.tyloo.serializer.ObjectSerializer;\nimport io.tyloo.utils.ByteUtils;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport org.apache.commons.lang3.time.DateUtils;\n\nimport java.text.ParseException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/*\n *\n * л\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:07 2019/5/4\n *\n */\n\npublic class ExpandTransactionSerializer {\n\n    public static Map<byte[], byte[]> serialize(ObjectSerializer serializer, Transaction transaction) throws CloneNotSupportedException {\n\n        Map<byte[], byte[]> map = new HashMap<>();\n\n        map.put(\"GLOBAL_TX_ID\".getBytes(), transaction.getXid().getGlobalTransactionId());\n        map.put(\"BRANCH_QUALIFIER\".getBytes(), transaction.getXid().getBranchQualifier());\n        map.put(\"STATUS\".getBytes(), ByteUtils.intToBytes(transaction.getStatus().getId()));\n        map.put(\"TRANSACTION_TYPE\".getBytes(), ByteUtils.intToBytes(transaction.getTransactionType().getId()));\n        map.put(\"RETRIED_COUNT\".getBytes(), ByteUtils.intToBytes(transaction.getRetriedCount()));\n        map.put(\"CREATE_TIME\".getBytes(), DateFormatUtils.format(transaction.getCreateTime(), \"yyyy-MM-dd HH:mm:ss\").getBytes());\n        map.put(\"LAST_UPDATE_TIME\".getBytes(), DateFormatUtils.format(transaction.getLastUpdateTime(), \"yyyy-MM-dd HH:mm:ss\").getBytes());\n        map.put(\"VERSION\".getBytes(), ByteUtils.longToBytes(transaction.getVersion()));\n        map.put(\"CONTENT\".getBytes(), serializer.serialize(transaction));\n        map.put(\"CONTENT_VIEW\".getBytes(), JSON.toJSONString(transaction).getBytes());\n        return map;\n    }\n\n    public static Transaction deserialize(ObjectSerializer serializer, Map<byte[], byte[]> map1) {\n\n        Map<String, byte[]> propertyMap = new HashMap<>();\n\n        for (Map.Entry<byte[], byte[]> entry : map1.entrySet()) {\n            propertyMap.put(new String(entry.getKey()), entry.getValue());\n        }\n\n        byte[] content = propertyMap.get(\"CONTENT\");\n        Transaction transaction = (Transaction) serializer.deserialize(content);\n        transaction.changeStatus(TransactionStatus.valueOf(ByteUtils.bytesToInt(propertyMap.get(\"STATUS\"))));\n        transaction.resetRetriedCount(ByteUtils.bytesToInt(propertyMap.get(\"RETRIED_COUNT\")));\n\n        try {\n            transaction.setLastUpdateTime(DateUtils.parseDate(new String(propertyMap.get(\"LAST_UPDATE_TIME\")), \"yyyy-MM-dd HH:mm:ss\"));\n        } catch (ParseException e) {\n            throw new SystemException(e);\n        }\n\n        transaction.setVersion(ByteUtils.bytesToLong(propertyMap.get(\"VERSION\")));\n        return transaction;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/helper/JedisCallback.java",
    "content": "package io.tyloo.repository.helper;\n\nimport redis.clients.jedis.Jedis;\n\n/*\n * Jedisصӿ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:07 2019/5/4\n *\n */\n\npublic interface JedisCallback<T> {\n\n    /**\n     * doInJedisΪṩһδװ jedis 󣬿ʹԭ jedis ĸַ\n     * @param jedis\n     * @return\n     */\n    public T doInJedis(Jedis jedis) throws CloneNotSupportedException;\n}"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/helper/RedisHelper.java",
    "content": "package io.tyloo.repository.helper;\n\nimport org.apache.log4j.Logger;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.exceptions.JedisDataException;\n\nimport javax.transaction.xa.Xid;\n\n/*\n *\n * Redis\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:07 2019/5/6\n *\n */\n\npublic class RedisHelper {\n\n    public static int SCAN_COUNT = 30;\n    public static String SCAN_TEST_PATTERN = \"*\";\n    public static String SCAN_INIT_CURSOR = \"0\";\n\n    private static final Logger logger = Logger.getLogger(RedisHelper.class);\n\n    public static byte[] getRedisKey(String keyPrefix, Xid xid) {\n        return (keyPrefix + xid.toString()).getBytes();\n    }\n\n    public static byte[] getRedisKey(String keyPrefix, String globalTransactionId, String branchQualifier) {\n        return (keyPrefix + globalTransactionId + \":\" + branchQualifier).getBytes();\n    }\n\n    /**\n     * JedisPoolÿJedis\n     *  @param <T>\n     * @param jedisPool\n     * @param callback\n     * @return\n     */\n    public static <T> T execute(JedisPool jedisPool, JedisCallback<T> callback) {\n        try (Jedis jedis = jedisPool.getResource()) {\n            return callback.doInJedis(jedis);\n        } catch (CloneNotSupportedException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * SCAN cursor [MATCH pattern] [COUNT count]\n     * $redis-cli scan 0 match key99* count 1000\n     * 1) \"13912\"\n     * 2)  1) \"key997\"\n     * 2) \"key9906\"\n     * 3) \"key9957\"\n     * 4) \"key9902\"\n     * 5) \"key9971\"\n     * 6) \"key9935\"\n     * 7) \"key9958\"\n     * 8) \"key9928\"\n     * 9) \"key9931\"\n     * 10) \"key9961\"\n     * 11) \"key9948\"\n     * 12) \"key9965\"\n     * 13) \"key9937\"\n     * <p>\n     * $redis-cli scan 13912 match key99* count 1000\n     * 1) \"5292\"\n     * 2)  1) \"key996\"\n     * 2) \"key9960\"\n     * 3) \"key9973\"\n     * 4) \"key9978\"\n     * 5) \"key9927\"\n     * 6) \"key995\"\n     * 7) \"key9992\"\n     * 8) \"key9993\"\n     * 9) \"key9964\"\n     * 10) \"key9934\"\n     * ؽΪ֣һּ 1) һεα꣬ڶּ 2) Ǳε\n     *\n     * @param pattern\n     * @param count\n     * @return\n     */\n    public static ScanParams buildDefaultScanParams(String pattern, int count) {\n        return new ScanParams().match(pattern).count(count);\n    }\n\n    /**\n     * Ƿ֧scanJedis\n     *\n     * @param jedis\n     * @return\n     */\n    public static Boolean isSupportScanCommand(Jedis jedis) {\n        try {\n            ScanParams scanParams = buildDefaultScanParams(SCAN_TEST_PATTERN, SCAN_COUNT);\n            jedis.scan(SCAN_INIT_CURSOR, scanParams);\n        } catch (JedisDataException e) {\n            logger.error(e.getMessage(), e);\n            logger.info(\"Redis **NOT** support scan command\");\n            return false;\n        }\n\n        logger.info(\"Redis support scan command\");\n        return true;\n    }\n}"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/repository/helper/TransactionSerializer.java",
    "content": "package io.tyloo.repository.helper;\n\nimport io.tyloo.Transaction;\nimport io.tyloo.serializer.ObjectSerializer;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/*\n *\n * л\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:08 2019/5/8\n *\n */\n\npublic class TransactionSerializer {\n\n    public static byte[] serialize(ObjectSerializer serializer, Transaction transaction) throws CloneNotSupportedException {\n        Map<String, Object> map = new HashMap<>();\n\n        map.put(\"GLOBAL_TX_ID\", transaction.getXid().getGlobalTransactionId());\n        map.put(\"BRANCH_QUALIFIER\", transaction.getXid().getBranchQualifier());\n        map.put(\"STATUS\", transaction.getStatus().getId());\n        map.put(\"TRANSACTION_TYPE\", transaction.getTransactionType().getId());\n        map.put(\"RETRIED_COUNT\", transaction.getRetriedCount());\n        map.put(\"CREATE_TIME\", transaction.getCreateTime());\n        map.put(\"LAST_UPDATE_TIME\", transaction.getLastUpdateTime());\n        map.put(\"VERSION\", transaction.getVersion());\n        map.put(\"CONTENT\", serializer.serialize(transaction));\n\n        return serializer.serialize(map);\n    }\n\n    public static Transaction deserialize(ObjectSerializer serializer, byte[] value) {\n\n        Map<String, Object> map = (Map<String, Object>) serializer.deserialize(value);\n\n        byte[] content = (byte[]) map.get(\"CONTENT\");\n        Transaction transaction = (Transaction) serializer.deserialize(content);\n        transaction.resetRetriedCount((Integer) map.get(\"RETRIED_COUNT\"));\n        transaction.setLastUpdateTime((Date) map.get(\"LAST_UPDATE_TIME\"));\n        transaction.setVersion((Long) map.get(\"VERSION\"));\n        return transaction;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/serializer/JacksonJsonSerializer.java",
    "content": "package io.tyloo.serializer;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\nimport io.tyloo.SystemException;\nimport io.tyloo.Transaction;\n\nimport java.io.IOException;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:29 2019/12/4\n *\n */\n\npublic class JacksonJsonSerializer implements ObjectSerializer<Transaction> {\n\n    private static ObjectMapper objectMapper = new ObjectMapper();\n\n    static {\n//        objectMapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL,\"@class\");\n        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);\n        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\n        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);\n\n    }\n\n    @Override\n    public byte[] serialize(Transaction transaction) {\n        try {\n            return objectMapper.writeValueAsBytes(transaction);\n        } catch (JsonProcessingException e) {\n            throw new SystemException(String.format(\"serialize object failed. object:%s\", transaction), e);\n        }\n    }\n\n    @Override\n    public Transaction deserialize(byte[] bytes) {\n        try {\n            return objectMapper.readValue(bytes, Transaction.class);\n        } catch (IOException e) {\n            throw new SystemException(String.format(\"deserialize object failed.\"), e);\n        }\n    }\n\n    @Override\n    public Transaction clone(Transaction object) {\n        return deserialize(serialize(object));\n    }\n\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/serializer/JdkSerializationSerializer.java",
    "content": "package io.tyloo.serializer;\n\nimport io.tyloo.Transaction;\nimport org.apache.commons.lang3.SerializationUtils;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:29 2019/5/22\n *\n */\n\npublic class JdkSerializationSerializer implements ObjectSerializer<Transaction> {\n\n    @Override\n    public byte[] serialize(Transaction object) {\n        return SerializationUtils.serialize(object);\n    }\n\n    @Override\n    public Transaction deserialize(byte[] bytes) {\n        if (bytes == null) {\n            return null;\n        } else {\n            return (Transaction) SerializationUtils.deserialize(bytes);\n        }\n    }\n\n    @Override\n    public Transaction clone(Transaction object) {\n        return SerializationUtils.clone(object);\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/serializer/KryoPoolSerializer.java",
    "content": "package io.tyloo.serializer;\n\nimport com.esotericsoftware.kryo.Kryo;\nimport com.esotericsoftware.kryo.io.Input;\nimport com.esotericsoftware.kryo.io.Output;\nimport com.esotericsoftware.kryo.pool.KryoCallback;\nimport com.esotericsoftware.kryo.pool.KryoFactory;\nimport com.esotericsoftware.kryo.pool.KryoPool;\nimport io.tyloo.Transaction;\nimport org.objenesis.strategy.StdInstantiatorStrategy;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:29 2019/5/22\n *\n */\n\npublic class KryoPoolSerializer implements ObjectSerializer<Transaction> {\n\n\n    static KryoFactory factory = () -> {\n        Kryo kryo = new Kryo();\n        kryo.setReferences(true);\n        kryo.setRegistrationRequired(false);\n        //Fix the NPE bug when deserializing Collections.\n        ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())\n                .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());\n\n        return kryo;\n    };\n\n\n    KryoPool pool = new KryoPool.Builder(factory).softReferences().build();\n\n    private int initPoolSize = 300;\n\n    public KryoPoolSerializer() {\n        init();\n    }\n\n    public KryoPoolSerializer(int initPoolSize) {\n        this.initPoolSize = initPoolSize;\n        init();\n    }\n\n    private void init() {\n\n        for (int i = 0; i < initPoolSize; i++) {\n            Kryo kryo = pool.borrow();\n            pool.release(kryo);\n        }\n    }\n\n    @Override\n    public byte[] serialize(final Transaction object) {\n\n        return pool.run(kryo -> {\n            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n            Output output = new Output(byteArrayOutputStream);\n\n            kryo.writeClassAndObject(output, object);\n            output.flush();\n\n            return byteArrayOutputStream.toByteArray();\n        });\n    }\n\n    @Override\n    public Transaction deserialize(final byte[] bytes) {\n\n        return pool.run(kryo -> {\n            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);\n            Input input = new Input(byteArrayInputStream);\n\n            return (Transaction) kryo.readClassAndObject(input);\n        });\n    }\n\n    @Override\n    public Transaction clone(final Transaction object) {\n        return pool.run(kryo -> kryo.copy(object));\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/serializer/ObjectSerializer.java",
    "content": "package io.tyloo.serializer;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:29 2019/5/22\n *\n */\npublic interface ObjectSerializer<T> {\n\n    /**\n     * Serialize the given object to binary data.\n     *\n     * @param t object to serialize\n     * @return the equivalent binary data\n     */\n    byte[] serialize(T t);\n\n    /**\n     * Deserialize an object from the given binary data.\n     *\n     * @param bytes object binary representation\n     * @return the equivalent object instance\n     */\n    T deserialize(byte[] bytes);\n\n\n    T clone(T object);\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/support/BeanFactory.java",
    "content": "package io.tyloo.support;\n\n/*\n *\n * Bean\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:34 2019/5/27\n *\n */\n\npublic interface BeanFactory {\n    <T> T getBean(Class<T> var1);\n\n    <T> boolean isFactoryOf(Class<T> clazz);\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/support/FactoryBuilder.java",
    "content": "package io.tyloo.support;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/*\n *\n * Bean\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:35 2019/5/27\n *\n */\npublic final class FactoryBuilder {\n\n\n    private FactoryBuilder() {\n\n    }\n\n    /**\n     * Bean \n     */\n    private static List<BeanFactory> beanFactories = new ArrayList<BeanFactory>();\n\n    /**\n     * Bean ӳ\n     */\n    private static ConcurrentHashMap<Class, SingeltonFactory> classFactoryMap = new ConcurrentHashMap<>();\n\n    /**\n     * ָ൥\n     *\n     * @param <T>   \n     * @param clazz ָ\n     * @return \n     */\n    public static <T> SingeltonFactory<T> factoryOf(Class<T> clazz) {\n\n        if (!classFactoryMap.containsKey(clazz)) {\n            for (BeanFactory beanFactory : beanFactories) {\n                if (beanFactory.isFactoryOf(clazz)) {\n                    classFactoryMap.putIfAbsent(clazz, new SingeltonFactory<T>(clazz, beanFactory.getBean(clazz)));\n                }\n            }\n            if (!classFactoryMap.containsKey(clazz)) {\n                classFactoryMap.putIfAbsent(clazz, new SingeltonFactory<T>(clazz));\n            }\n        }\n\n        return classFactoryMap.get(clazz);\n    }\n\n    /**\n     * BeanעᵽǰBuilder\n     *\n     * @param beanFactory Bean\n     */\n    public static void registerBeanFactory(BeanFactory beanFactory) {\n        beanFactories.add(beanFactory);\n    }\n\n    /**\n     * \n     *\n     * @param <T> \n     */\n    public static class SingeltonFactory<T> {\n\n        private volatile T instance = null;\n\n        private String className;\n\n        public SingeltonFactory(Class<T> clazz, T instance) {\n            this.className = clazz.getName();\n            this.instance = instance;\n        }\n\n        public SingeltonFactory(Class<T> clazz) {\n            this.className = clazz.getName();\n        }\n\n        /**\n         * õ\n         *\n         * @return \n         */\n        public T getInstance() {\n            if (instance == null) {\n                synchronized (SingeltonFactory.class) {\n                    if (instance == null) {\n                        try {\n                            ClassLoader loader = Thread.currentThread().getContextClassLoader();\n                            Class<?> clazz = loader.loadClass(className);\n                            instance = (T) clazz.newInstance();\n                        } catch (Exception e) {\n                            throw new RuntimeException(\"Failed to create an instance of \" + className, e);\n                        }\n                    }\n                }\n            }\n\n            return instance;\n        }\n\n        @Override\n        public boolean equals(Object other) {\n            if (this == other) {\n                return true;\n            }\n            if (other == null || getClass() != other.getClass()) {\n                return false;\n            }\n\n            SingeltonFactory that = (SingeltonFactory) other;\n\n            return className.equals(that.className);\n        }\n\n        @Override\n        public int hashCode() {\n            return className.hashCode();\n        }\n    }\n}"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/support/TransactionConfigurator.java",
    "content": "package io.tyloo.support;\n\nimport io.tyloo.TransactionManager;\nimport io.tyloo.TransactionRepository;\nimport io.tyloo.recover.TylooRecoverConfiguration;\n\n/*\n *\n * \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:34 2019/5/28\n *\n */\n\npublic interface TransactionConfigurator {\n\n    /**\n     * ȡ.\n     *\n     * @return\n     */\n    TransactionManager getTransactionManager();\n\n    /**\n     * ȡ.\n     *\n     * @return\n     */\n    TransactionRepository getTransactionRepository();\n\n    /**\n     * ȡָ.\n     *\n     * @return\n     */\n    TylooRecoverConfiguration getTylooRecoverConfiguration();\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/utils/ByteUtils.java",
    "content": "package io.tyloo.utils;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:49 2019/5/30\n *\n */\npublic class ByteUtils {\n\n    public static byte[] longToBytes(long num) {\n        return String.valueOf(num).getBytes();\n    }\n\n    public static long bytesToLong(byte[] bytes) {\n        return Long.valueOf(new String(bytes));\n    }\n\n    public static byte[] intToBytes(int num) {\n        return String.valueOf(num).getBytes();\n    }\n\n    public static int bytesToInt(byte[] bytes) {\n        return Integer.valueOf(new String(bytes));\n    }\n\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/utils/CollectionUtils.java",
    "content": "package io.tyloo.utils;\n\nimport java.util.Collection;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:49 2019/5/30\n *\n */\npublic final class CollectionUtils {\n\n    private CollectionUtils() {\n\n    }\n\n    public static boolean isEmpty(Collection collection) {\n        return (collection == null || collection.isEmpty());\n    }\n}"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/utils/ReflectionUtils.java",
    "content": "package io.tyloo.utils;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\nimport java.util.Map;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 18:07 2019/5/19\n *\n */\npublic class ReflectionUtils {\n\n\n    public static void makeAccessible(Method method) {\n\n        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {\n            method.setAccessible(true);\n        }\n    }\n\n    public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {\n\n        Object handler = Proxy.getInvocationHandler(annotation);\n        Field f;\n        f = handler.getClass().getDeclaredField(\"memberValues\");\n        f.setAccessible(true);\n        Map<String, Object> memberValues;\n        memberValues = (Map<String, Object>) f.get(handler);\n        Object oldValue = memberValues.get(key);\n        if (oldValue == null || oldValue.getClass() != newValue.getClass()) {\n            throw new IllegalArgumentException();\n        }\n        memberValues.put(key, newValue);\n        return oldValue;\n    }\n\n    public static Class getDeclaringType(Class aClass, String methodName, Class<?>[] parameterTypes) {\n\n        Method method = null;\n        Class findClass = aClass;\n        do {\n            Class[] clazzes = findClass.getInterfaces();\n            for (Class clazz : clazzes) {\n                try {\n                    method = clazz.getDeclaredMethod(methodName, parameterTypes);\n                } catch (NoSuchMethodException e) {\n                    method = null;\n                }\n                if (method != null) {\n                    return clazz;\n                }\n            }\n            findClass = findClass.getSuperclass();\n        } while (!findClass.equals(Object.class));\n        return aClass;\n    }\n\n    public static Object getNullValue(Class type) {\n\n        if (boolean.class.equals(type)) {\n            return false;\n        } else if (byte.class.equals(type)) {\n            return 0;\n        } else if (short.class.equals(type)) {\n            return 0;\n        } else if (int.class.equals(type)) {\n            return 0;\n        } else if (long.class.equals(type)) {\n            return 0;\n        } else if (float.class.equals(type)) {\n            return 0;\n        } else if (double.class.equals(type)) {\n            return 0;\n        } else if (char.class.equals(type)) {\n            return ' ';\n        }\n\n        return null;\n    }\n}"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/utils/StringUtils.java",
    "content": "package io.tyloo.utils;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:49 2019/5/30\n *\n */\npublic class StringUtils {\n\n    public static boolean isNotEmpty(String value) {\n\n        if(value == null) {\n            return false;\n        }\n\n        if(value.equals(\"\")) {\n            return false;\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/utils/TransactionUtils.java",
    "content": "package io.tyloo.utils;\n\nimport io.tyloo.api.Propagation;\nimport io.tyloo.interceptor.TylooMethodContext;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:49 2019/5/30\n *\n */\npublic class TransactionUtils {\n\n    public static boolean isLegalTransactionContext(boolean isTransactionActive, TylooMethodContext tylooMethodContext) {\n\n        return !tylooMethodContext.getPropagation().equals(Propagation.MANDATORY) ||\n                isTransactionActive ||\n                tylooMethodContext.getTransactionContext() != null;\n    }\n}\n"
  },
  {
    "path": "tyloo-core/src/main/java/io/tyloo/utils/TylooMethodUtils.java",
    "content": "package io.tyloo.utils;\n\nimport io.tyloo.api.Propagation;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.common.MethodRole;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\n\nimport java.lang.reflect.Method;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 19:49 2019/5/30\n *\n */\npublic class TylooMethodUtils {\n\n    public static Method getTylooMethod(ProceedingJoinPoint pjp) {\n\n        Method method = ((MethodSignature) (pjp.getSignature())).getMethod();\n        if (method.getAnnotation(Tyloo.class) == null) {\n            try {\n                method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());\n            } catch (NoSuchMethodException e) {\n                return null;\n            }\n        }\n        return method;\n    }\n\n    public static MethodRole calculateMethodType(Propagation propagation, boolean isTransactionActive, TransactionContext transactionContext) {\n\n        if ((propagation.equals(Propagation.REQUIRED) && !isTransactionActive && transactionContext == null) ||\n                propagation.equals(Propagation.REQUIRES_NEW)) {\n            return MethodRole.ROOT;\n        } else if ((propagation.equals(Propagation.REQUIRED) || propagation.equals(Propagation.MANDATORY)) && !isTransactionActive && transactionContext != null) {\n            return MethodRole.PROVIDER;\n        } else {\n            return MethodRole.NORMAL;\n        }\n    }\n\n    public static int getTransactionContextParamPosition(Class<?>[] parameterTypes) {\n\n        int position = -1;\n        for (int i = 0; i < parameterTypes.length; i++) {\n            if (parameterTypes[i].equals(TransactionContext.class)) {\n                position = i;\n                break;\n            }\n        }\n        return position;\n    }\n}\n"
  },
  {
    "path": "tyloo-dubbo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-core</artifactId>\n        </dependency>\n\n\n\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-dependencies-zookeeper</artifactId>\n            <type>pom</type>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/constants/TransactionContextConstants.java",
    "content": "package io.tyloo.dubbo.constants;\n\n/*\n *\n * ĳ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 9:33 2019/7/5\n *\n */\n\n\npublic class TransactionContextConstants {\n    public static final String TRANSACTION_CONTEXT = \"TRANSACTION_CONTEXT\";\n}\n"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/context/DubboTransactionContextEditor.java",
    "content": "package io.tyloo.dubbo.context;\n\nimport com.alibaba.fastjson.JSON;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.api.TransactionContextEditor;\nimport io.tyloo.dubbo.constants.TransactionContextConstants;\nimport org.apache.dubbo.common.utils.StringUtils;\nimport org.apache.dubbo.rpc.RpcContext;\n\nimport java.lang.reflect.Method;\n\n/*\n * Dubbo ı༭\n *\n * ײʹõdubboTransactionContextEditorΪDubboTransactionContextEditor.classʹdubboʽηʽ\n * ͨ Dubbo ʽεķʽ Dubbo Service ӿ TransactionContext Խӿڲһ\n * tyloo ͨ Dubbo Proxy Ļƣʵ `@Tyloo` Զ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 15:32 2019/7/17\n *\n */\n\npublic class DubboTransactionContextEditor implements TransactionContextEditor {\n    @Override\n    public TransactionContext get(Object target, Method method, Object[] args) {\n\n        String context = RpcContext.getContext().getAttachment(TransactionContextConstants.TRANSACTION_CONTEXT);\n\n        if (StringUtils.isNotEmpty(context)) {\n            return JSON.parseObject(context, TransactionContext.class);\n        }\n\n        return null;\n    }\n\n    @Override\n    public void set(TransactionContext transactionContext, Object target, Method method, Object[] args) {\n\n        RpcContext.getContext().setAttachment(TransactionContextConstants.TRANSACTION_CONTEXT, JSON.toJSONString(transactionContext));\n    }\n}\n"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/javassist/TylooClassGenerator.java",
    "content": "package io.tyloo.dubbo.proxy.javassist;\n\nimport javassist.*;\nimport javassist.bytecode.AnnotationsAttribute;\nimport javassist.bytecode.ConstPool;\nimport javassist.bytecode.annotation.Annotation;\nimport javassist.bytecode.annotation.ClassMemberValue;\nimport javassist.bytecode.annotation.EnumMemberValue;\nimport javassist.bytecode.annotation.StringMemberValue;\nimport org.apache.dubbo.common.utils.ReflectUtils;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicLong;\n\n\n/*\n *\n * TCC  Javassist ʵ֡\n *      Javassist һԴķ༭ʹ Java ֽ⡣\n * һ Dubbo ServiceTccProxy ᶯ̬ࣺ\n        - Dubbo Service  Proxy\n        - Dubbo Service  ProxyFactoryɶӦ Dubbo Service Proxy\n *\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 9:33 2019/8/1\n *\n */\n\npublic final class TylooClassGenerator {\n    private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0);\n    private static final String SIMPLE_NAME_TAG = \"<init>\";\n    private static final Map<ClassLoader, ClassPool> POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>(); //ClassLoader - ClassPool\n    private final Set<String> tylooMethods = new HashSet<String>();\n    private ClassPool mPool;\n    private CtClass mCtc;\n    private String mClassName, mSuperClass;\n    private Set<String> mInterfaces;\n    private List<String> mFields, mConstructors, mMethods;\n    private Map<String, Method> mCopyMethods; // <method desc,method instance>\n    private Map<String, Constructor<?>> mCopyConstructors; // <constructor desc,constructor instance>\n    private boolean mDefaultConstructor = false;\n\n    private TylooClassGenerator() {\n    }\n\n    private TylooClassGenerator(ClassPool pool) {\n        mPool = pool;\n    }\n\n    public static TylooClassGenerator newInstance() {\n        return new TylooClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));\n    }\n\n    public static TylooClassGenerator newInstance(ClassLoader loader) {\n        return new TylooClassGenerator(getClassPool(loader));\n    }\n\n    public static boolean isDynamicClass(Class<?> cl) {\n        return TylooClassGenerator.DC.class.isAssignableFrom(cl);\n    }\n\n    public static ClassPool getClassPool(ClassLoader loader) {\n        if (loader == null) {\n            return ClassPool.getDefault();\n        }\n\n        ClassPool pool = POOL_MAP.get(loader);\n        if (pool == null) {\n            pool = new ClassPool(true);\n            pool.appendClassPath(new LoaderClassPath(loader));\n            POOL_MAP.put(loader, pool);\n        }\n        return pool;\n    }\n\n    private static String modifier(int mod) {\n        if (java.lang.reflect.Modifier.isPublic(mod)) {\n            return \"public\";\n        }\n        if (java.lang.reflect.Modifier.isProtected(mod)) {\n            return \"protected\";\n        }\n        if (java.lang.reflect.Modifier.isPrivate(mod)) {\n            return \"private\";\n        }\n        return \"\";\n    }\n\n    public String getClassName() {\n        return mClassName;\n    }\n\n    public TylooClassGenerator setClassName(String name) {\n        mClassName = name;\n        return this;\n    }\n\n    public TylooClassGenerator addInterface(String cn) {\n        if (mInterfaces == null) {\n            mInterfaces = new HashSet<String>();\n        }\n        mInterfaces.add(cn);\n        return this;\n    }\n\n    public TylooClassGenerator addInterface(Class<?> cl) {\n        return addInterface(cl.getName());\n    }\n\n    public TylooClassGenerator setSuperClass(String cn) {\n        mSuperClass = cn;\n        return this;\n    }\n\n    public TylooClassGenerator setSuperClass(Class<?> cl) {\n        mSuperClass = cl.getName();\n        return this;\n    }\n\n    public TylooClassGenerator addField(String code) {\n        if (mFields == null) {\n            mFields = new ArrayList<String>();\n        }\n        mFields.add(code);\n        return this;\n    }\n\n    public TylooClassGenerator addField(String name, int mod, Class<?> type) {\n        return addField(name, mod, type, null);\n    }\n\n    public TylooClassGenerator addField(String name, int mod, Class<?> type, String def) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' ');\n        sb.append(name);\n        if (def != null && def.length() > 0) {\n            sb.append('=');\n            sb.append(def);\n        }\n        sb.append(';');\n        return addField(sb.toString());\n    }\n\n    public TylooClassGenerator addMethod(String code) {\n        if (mMethods == null) {\n            mMethods = new ArrayList<String>();\n        }\n        mMethods.add(code);\n        return this;\n    }\n\n    public TylooClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, String body) {\n        return addMethod(false, name, mod, rt, pts, null, body);\n    }\n\n    public TylooClassGenerator addMethod(boolean isTylooMethod, String name, int mod, Class<?> rt, Class<?>[] pts, Class<?>[] ets, String body) {\n        StringBuilder sb = new StringBuilder();\n\n        sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);\n        sb.append('(');\n        for (int i = 0; i < pts.length; i++) {\n            if (i > 0) {\n                sb.append(',');\n            }\n            sb.append(ReflectUtils.getName(pts[i]));\n            sb.append(\" arg\").append(i);\n        }\n        sb.append(')');\n        if (ets != null && ets.length > 0) {\n            sb.append(\" throws \");\n            for (int i = 0; i < ets.length; i++) {\n                if (i > 0) {\n                    sb.append(',');\n                }\n                sb.append(ReflectUtils.getName(ets[i]));\n            }\n        }\n        sb.append('{').append(body).append('}');\n\n        if (isTylooMethod) {\n            tylooMethods.add(sb.toString());\n        }\n\n        return addMethod(sb.toString());\n    }\n\n    public TylooClassGenerator addMethod(Method m) {\n        addMethod(m.getName(), m);\n        return this;\n    }\n\n    public TylooClassGenerator addMethod(String name, Method m) {\n        String desc = name + ReflectUtils.getDescWithoutMethodName(m);\n        addMethod(':' + desc);\n        if (mCopyMethods == null) {\n            mCopyMethods = new ConcurrentHashMap<String, Method>(8);\n        }\n        mCopyMethods.put(desc, m);\n        return this;\n    }\n\n    public TylooClassGenerator addConstructor(String code) {\n        if (mConstructors == null) {\n            mConstructors = new LinkedList<String>();\n        }\n        mConstructors.add(code);\n        return this;\n    }\n\n    public TylooClassGenerator addConstructor(int mod, Class<?>[] pts, String body) {\n        return addConstructor(mod, pts, null, body);\n    }\n\n    public TylooClassGenerator addConstructor(int mod, Class<?>[] pts, Class<?>[] ets, String body) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG);\n        sb.append('(');\n        for (int i = 0; i < pts.length; i++) {\n            if (i > 0) {\n                sb.append(',');\n            }\n            sb.append(ReflectUtils.getName(pts[i]));\n            sb.append(\" arg\").append(i);\n        }\n        sb.append(')');\n        if (ets != null && ets.length > 0) {\n            sb.append(\" throws \");\n            for (int i = 0; i < ets.length; i++) {\n                if (i > 0) {\n                    sb.append(',');\n                }\n                sb.append(ReflectUtils.getName(ets[i]));\n            }\n        }\n        sb.append('{').append(body).append('}');\n        return addConstructor(sb.toString());\n    }\n\n    public TylooClassGenerator addConstructor(Constructor<?> c) {\n        String desc = ReflectUtils.getDesc(c);\n        addConstructor(\":\" + desc);\n        if (mCopyConstructors == null) {\n            mCopyConstructors = new ConcurrentHashMap<String, Constructor<?>>(4);\n        }\n        mCopyConstructors.put(desc, c);\n        return this;\n    }\n\n    public TylooClassGenerator addDefaultConstructor() {\n        mDefaultConstructor = true;\n        return this;\n    }\n\n    public ClassPool getClassPool() {\n        return mPool;\n    }\n\n    public Class<?> toClass() {\n        if (mCtc != null) {\n            mCtc.detach();\n        }\n        long id = CLASS_NAME_COUNTER.getAndIncrement();\n        try {\n            CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);\n            if (mClassName == null) {\n                mClassName = (mSuperClass == null || Modifier.isPublic(ctcs.getModifiers())\n                        ? TylooClassGenerator.class.getName() : mSuperClass + \"$sc\") + id;\n            }\n            mCtc = mPool.makeClass(mClassName);\n            if (mSuperClass != null) {\n                mCtc.setSuperclass(ctcs);\n            }\n            mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.\n            if (mInterfaces != null) {\n                for (String cl : mInterfaces) {\n                    mCtc.addInterface(mPool.get(cl));\n                }\n            }\n            if (mFields != null) {\n                for (String code : mFields) {\n                    mCtc.addField(CtField.make(code, mCtc));\n                }\n            }\n            if (mMethods != null) {\n                for (String code : mMethods) {\n                    if (code.charAt(0) == ':') {\n                        mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));\n                    } else {\n\n                        CtMethod ctMethod = CtNewMethod.make(code, mCtc);\n\n                        // עⷽ\n                        if (tylooMethods.contains(code)) {\n\n                            ConstPool constpool = mCtc.getClassFile().getConstPool();\n                            AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);\n                            Annotation annot = new Annotation(\"Tyloo\", constpool);\n                            EnumMemberValue enumMemberValue = new EnumMemberValue(constpool);\n                            enumMemberValue.setType(\"Propagation\");\n                            enumMemberValue.setValue(\"SUPPORTS\");\n                            annot.addMemberValue(\"propagation\", enumMemberValue);\n                            annot.addMemberValue(\"confirmMethod\", new StringMemberValue(ctMethod.getName(), constpool));\n                            annot.addMemberValue(\"cancelMethod\", new StringMemberValue(ctMethod.getName(), constpool));\n\n                            ClassMemberValue classMemberValue = new ClassMemberValue(\"DubboTransactionContextEditor\", constpool);\n                            annot.addMemberValue(\"transactionContextEditor\", classMemberValue);\n\n                            attr.addAnnotation(annot);\n                            ctMethod.getMethodInfo().addAttribute(attr);\n                        }\n\n                        mCtc.addMethod(ctMethod);\n                    }\n                }\n            }\n            if (mDefaultConstructor) {\n                mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));\n            }\n            if (mConstructors != null) {\n                for (String code : mConstructors) {\n                    if (code.charAt(0) == ':') {\n                        mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));\n                    } else {\n                        String[] sn = mCtc.getSimpleName().split(\"\\\\$+\"); // inner class name include $.\n                        mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length - 1]), mCtc));\n                    }\n                }\n            }\n            return mCtc.toClass();\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (NotFoundException e) {\n            throw new RuntimeException(e.getMessage(), e);\n        } catch (CannotCompileException e) {\n            throw new RuntimeException(e.getMessage(), e);\n        }\n    }\n\n    public void release() {\n        if (mCtc != null) {\n            mCtc.detach();\n        }\n        if (mInterfaces != null) {\n            mInterfaces.clear();\n        }\n        if (mFields != null) {\n            mFields.clear();\n        }\n        if (mMethods != null) {\n            mMethods.clear();\n        }\n        if (mConstructors != null) {\n            mConstructors.clear();\n        }\n        if (mCopyMethods != null) {\n            mCopyMethods.clear();\n        }\n        if (mCopyConstructors != null) {\n            mCopyConstructors.clear();\n        }\n    }\n\n    private CtClass getCtClass(Class<?> c) throws NotFoundException {\n        return mPool.get(c.getName());\n    }\n\n    private CtMethod getCtMethod(Method m) throws NotFoundException {\n        return getCtClass(m.getDeclaringClass()).getMethod(m.getName(), ReflectUtils.getDescWithoutMethodName(m));\n    }\n\n    private CtConstructor getCtConstructor(Constructor<?> c) throws NotFoundException {\n        return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c));\n    }\n\n    public interface DC {\n    } // dynamic class tag interface.\n}"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/javassist/TylooJavassistProxyFactory.java",
    "content": "package io.tyloo.dubbo.proxy.javassist;\n\nimport org.apache.dubbo.rpc.Invoker;\nimport org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;\nimport org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory;\n\n/*\n *\n *\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 9:33 2019/8/16\n *\n */\npublic class TylooJavassistProxyFactory extends JavassistProxyFactory {\n\n    /**\n     * ĿʱgetProxy(...)`  Dubbo Service  Proxy\n     * com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler`Dubbo ô\n     *\n     * @param invoker\n     * @param interfaces\n     * @param <T>\n     * @return\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {\n        return (T) TylooProxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));\n    }\n}\n"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/javassist/TylooProxy.java",
    "content": "/*\n * Copyright 1999-2011 Alibaba Group.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.tyloo.dubbo.proxy.javassist;\n\nimport io.tyloo.api.Tyloo;\nimport org.apache.dubbo.common.utils.ClassHelper;\nimport org.apache.dubbo.common.utils.ReflectUtils;\n\nimport java.lang.ref.Reference;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.*;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/*\n *\n * TCC Proxy  Dubbo Service  Proxy\n *\n * ο Dubbo Դʵ֣\n *     com.alibaba.dubbo.Enums.bytecode.Proxy\n *     com.alibaba.dubbo.Enums.bytecode.ClassGenerator\n *     com.alibaba.dubbo.Enums.bytecode.Wrapper\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 9:34 2019/8/27\n *\n */\npublic abstract class TylooProxy {\n    public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler() {\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args) {\n            return null;\n        }\n    };\n    public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = (proxy, method, args) -> {\n        throw new UnsupportedOperationException(\"Method [\" + ReflectUtils.getName(method) + \"] unimplemented.\");\n    };\n    private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);\n    private static final String PACKAGE_NAME = TylooProxy.class.getPackage().getName();\n    private static final Map<ClassLoader, Map<String, Object>> ProxyCacheMap = new WeakHashMap<ClassLoader, Map<String, Object>>();\n\n    private static final Object PendingGenerationMarker = new Object();\n\n    protected TylooProxy() {\n    }\n\n    /**\n     * TCC Proxy \n     *\n     * @param ics interface class array.\n     * @return TccProxy instance.\n     */\n    public static TylooProxy getProxy(Class<?>... ics) {\n        return getProxy(ClassHelper.getCallerClassLoader(TylooProxy.class), ics);\n    }\n\n    /**\n     * 1. Уӿ\n     * 2. ʹýӿڼ `;` ָƴӣΪ Proxy Ψһʶ\n     * 3.  Proxy Ӧ ClassLoader\n     * 4. һֱ Tyloo Proxy ֱɹ\n     * 5.  Dubbo Service  ProxyFactoryProxy Ĵ\n     * 6.  Dubbo Service  Proxy Ĵ롣\n     *\n     * @param cl\n     * @param ics\n     * @return\n     */\n    public static TylooProxy getProxy(ClassLoader cl, Class<?>... ics) {\n        if (ics.length > 65535) {\n            throw new IllegalArgumentException(\"interface limit exceeded\");\n        }\n\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < ics.length; i++) {\n            String itf = ics[i].getName();\n            if (!ics[i].isInterface()) {\n                throw new RuntimeException(itf + \" is not a interface.\");\n            }\n\n            Class<?> tmp = null;\n            try {\n                tmp = Class.forName(itf, false, cl);\n            } catch (ClassNotFoundException e) {\n            }\n\n            if (tmp != ics[i]) {\n                throw new IllegalArgumentException(ics[i] + \" is not visible from class loader\");\n            }\n\n            sb.append(itf).append(';');\n        }\n\n        // use interface class name list as key.\n        String key = sb.toString();\n\n        // get cache by class loader.\n        Map<String, Object> cache;\n        synchronized (ProxyCacheMap) {\n            cache = ProxyCacheMap.get(cl);\n            if (cache == null) {\n                cache = new HashMap<String, Object>();\n                ProxyCacheMap.put(cl, cache);\n            }\n        }\n\n        TylooProxy proxy = null;\n        synchronized (cache) {\n            do {\n                Object value = cache.get(key);\n                if (value instanceof Reference<?>) {\n                    proxy = (TylooProxy) ((Reference<?>) value).get();\n                    if (proxy != null) {\n                        return proxy;\n                    }\n                }\n\n                if (value == PendingGenerationMarker) {\n                    try {\n                        cache.wait();\n                    } catch (InterruptedException e) {\n                    }\n                } else {\n                    cache.put(key, PendingGenerationMarker);\n                    break;\n                }\n            }\n            while (true);\n        }\n\n        /**\n         *  Dubbo Service  Proxy Ĵ\n         */\n        long id = PROXY_CLASS_COUNTER.getAndIncrement();\n        String pkg = null;\n        TylooClassGenerator ccp = null, ccm = null;\n        try {\n            ccp = TylooClassGenerator.newInstance(cl);\n\n            Set<String> worked = new HashSet<String>();\n            List<Method> methods = new ArrayList<Method>();\n\n            for (int i = 0; i < ics.length; i++) {\n                if (!Modifier.isPublic(ics[i].getModifiers())) {\n                    String npkg = ics[i].getPackage().getName();\n                    if (pkg == null) {\n                        pkg = npkg;\n                    } else {\n                        if (pkg.equals(npkg)) {\n                        } else {\n                            throw new IllegalArgumentException(\"non-public interfaces from different packages\");\n                        }\n                    }\n                }\n                ccp.addInterface(ics[i]);\n\n                for (Method method : ics[i].getMethods()) {\n                    String desc = ReflectUtils.getDesc(method);\n                    if (worked.contains(desc)) {\n                        continue;\n                    }\n                    worked.add(desc);\n\n                    int ix = methods.size();\n                    Class<?> rt = method.getReturnType();\n                    Class<?>[] pts = method.getParameterTypes();\n\n                    StringBuilder code = new StringBuilder(\"Object[] args = new Object[\").append(pts.length).append(\"];\");\n                    for (int j = 0; j < pts.length; j++) {\n                        code.append(\" args[\").append(j).append(\"] = ($w)$\").append(j + 1).append(\";\");\n                    }\n                    code.append(\" Object ret = handler.invoke(this, methods[\" + ix + \"], args);\");\n                    if (!Void.TYPE.equals(rt)) {\n                        code.append(\" return \").append(asArgument(rt, \"ret\")).append(\";\");\n                    }\n\n                    methods.add(method);\n\n                    StringBuilder tylooDesc = new StringBuilder();\n\n                    Tyloo tyloo = method.getAnnotation(Tyloo.class);\n\n                    ccp.addMethod(tyloo != null, method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());\n                }\n            }\n\n            if (pkg == null) {\n                pkg = PACKAGE_NAME;\n            }\n\n            // create ProxyInstance class.\n            String pcn = pkg + \".proxy\" + id;\n            ccp.setClassName(pcn);\n            ccp.addField(\"public static java.lang.reflect.Method[] methods;\");\n            ccp.addField(\"private \" + InvocationHandler.class.getName() + \" handler;\");\n            ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], \"handler=$1;\");\n            ccp.addDefaultConstructor();\n            Class<?> clazz = ccp.toClass();\n            clazz.getField(\"methods\").set(null, methods.toArray(new Method[0]));\n\n            // create TccProxy class.\n            String fcn = TylooProxy.class.getName() + id;\n            ccm = TylooClassGenerator.newInstance(cl);\n            ccm.setClassName(fcn);\n            ccm.addDefaultConstructor();\n            ccm.setSuperClass(TylooProxy.class);\n            ccm.addMethod(\"public Object newInstance(\" + InvocationHandler.class.getName() + \" h){ return new \" + pcn + \"($1); }\");\n            Class<?> pc = ccm.toClass();\n            proxy = (TylooProxy) pc.newInstance();\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new RuntimeException(e.getMessage(), e);\n        } finally {\n            // release TccClassGenerator\n            if (ccp != null) {\n                ccp.release();\n            }\n            if (ccm != null) {\n                ccm.release();\n            }\n            synchronized (cache) {\n                if (proxy == null) {\n                    cache.remove(key);\n                } else {\n                    cache.put(key, new WeakReference<TylooProxy>(proxy));\n                }\n                cache.notifyAll();\n            }\n        }\n        return proxy;\n    }\n\n    private static String asArgument(Class<?> cl, String name) {\n        if (cl.isPrimitive()) {\n            if (Boolean.TYPE == cl) {\n                return name + \"==null?false:((Boolean)\" + name + \").booleanValue()\";\n            }\n            if (Byte.TYPE == cl) {\n                return name + \"==null?(byte)0:((Byte)\" + name + \").byteValue()\";\n            }\n            if (Character.TYPE == cl) {\n                return name + \"==null?(char)0:((Character)\" + name + \").charValue()\";\n            }\n            if (Double.TYPE == cl) {\n                return name + \"==null?(double)0:((Double)\" + name + \").doubleValue()\";\n            }\n            if (Float.TYPE == cl) {\n                return name + \"==null?(float)0:((Float)\" + name + \").floatValue()\";\n            }\n            if (Integer.TYPE == cl) {\n                return name + \"==null?(int)0:((Integer)\" + name + \").intValue()\";\n            }\n            if (Long.TYPE == cl) {\n                return name + \"==null?(long)0:((Long)\" + name + \").longValue()\";\n            }\n            if (Short.TYPE == cl) {\n                return name + \"==null?(short)0:((Short)\" + name + \").shortValue()\";\n            }\n            throw new RuntimeException(name + \" is unknown primitive type.\");\n        }\n        return \"(\" + ReflectUtils.getName(cl) + \")\" + name;\n    }\n\n    /**\n     * get instance with default handler.\n     *\n     * @return instance.\n     */\n    public Object newInstance() {\n        return newInstance(THROW_UNSUPPORTED_INVOKER);\n    }\n\n    /**\n     * get instance with special handler.\n     *\n     * @return instance.\n     */\n    abstract public Object newInstance(InvocationHandler handler);\n}"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/jdk/MethodProceedingJoinPoint.java",
    "content": "package io.tyloo.dubbo.proxy.jdk;\n\nimport io.tyloo.SystemException;\nimport io.tyloo.utils.ReflectionUtils;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.Signature;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.aspectj.lang.reflect.SourceLocation;\nimport org.aspectj.runtime.internal.AroundClosure;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\n\n\n/*\n *\n * ɷ\n *\n * ο [`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) ʵ֡\n * 洦ɺ󣬵 `#proceed(...)` Զ Dubbo Service á\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 10:56 2019/9/12\n *\n */\n\npublic class MethodProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {\n\n    private final Object proxy;\n\n    private final Object target;\n\n    private final Method method;\n\n    private final Object[] args;\n\n    private Signature signature;\n\n    /**\n     * Lazily initialized source location object\n     */\n    private SourceLocation sourceLocation;\n\n    /**\n     * @param proxy  \n     * @param target Ŀ\n     * @param method \n     * @param args   \n     */\n    public MethodProceedingJoinPoint(Object proxy, Object target, Method method, Object[] args) {\n        this.proxy = proxy;\n        this.target = target;\n        this.method = method;\n        this.args = args;\n    }\n\n    /**\n     * Զ Dubbo Service \n     *\n     * @return\n     * @throws Throwable\n     */\n    @Override\n    public void set$AroundClosure(AroundClosure aroundClosure) {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Object proceed() throws Throwable {\n\n        // Use reflection to invoke the method.\n        try {\n            ReflectionUtils.makeAccessible(method);\n            return method.invoke(target, args);\n        } catch (InvocationTargetException ex) {\n            // Invoked method threw a checked exception.\n            // We must rethrow it. The client won't see the interceptor.\n            throw ex.getTargetException();\n        } catch (IllegalArgumentException ex) {\n            throw new SystemException(\"Tried calling method [\" +\n                    method + \"] on target [\" + target + \"] failed\", ex);\n        } catch (IllegalAccessException ex) {\n            throw new SystemException(\"Could not access method [\" + method + \"]\", ex);\n        }\n    }\n\n    @Override\n    public Object proceed(Object[] objects) throws Throwable {\n\n        // Use reflection to invoke the method.\n        try {\n            ReflectionUtils.makeAccessible(method);\n            return method.invoke(target, objects);\n        } catch (InvocationTargetException ex) {\n            throw ex.getTargetException();\n        } catch (IllegalArgumentException ex) {\n            throw new SystemException(\"Tried calling method [\" +\n                    method + \"] on target [\" + target + \"] failed\", ex);\n        } catch (IllegalAccessException ex) {\n            throw new SystemException(\"Could not access method [\" + method + \"]\", ex);\n        }\n    }\n\n    @Override\n    public String toShortString() {\n        return \"execution(\" + getSignature().toShortString() + \")\";\n    }\n\n    @Override\n    public String toLongString() {\n        return \"execution(\" + getSignature().toLongString() + \")\";\n    }\n\n    @Override\n    public String toString() {\n        return \"execution(\" + getSignature().toString() + \")\";\n    }\n\n    @Override\n    public Object getThis() {\n        return this.proxy;\n    }\n\n    @Override\n    public Object getTarget() {\n        return this.target;\n    }\n\n    @Override\n    public Object[] getArgs() {\n        return this.args;\n    }\n\n    @Override\n    public Signature getSignature() {\n        if (this.signature == null) {\n            this.signature = new MethodSignatureImpl();\n        }\n        return signature;\n    }\n\n    @Override\n    public SourceLocation getSourceLocation() {\n        if (this.sourceLocation == null) {\n            this.sourceLocation = new SourceLocationImpl();\n        }\n        return this.sourceLocation;\n    }\n\n    @Override\n    public String getKind() {\n        return ProceedingJoinPoint.METHOD_EXECUTION;\n    }\n\n    @Override\n    public int getId() {\n        return 0;\n    }\n\n    @Override\n    public StaticPart getStaticPart() {\n        return this;\n    }\n\n    /**\n     * Lazily initialized MethodSignature.\n     */\n    private class MethodSignatureImpl implements MethodSignature {\n\n        private volatile String[] parameterNames;\n\n        @Override\n        public String getName() {\n            return method.getName();\n        }\n\n        @Override\n        public int getModifiers() {\n            return method.getModifiers();\n        }\n\n        @Override\n        public Class getDeclaringType() {\n            return method.getDeclaringClass();\n        }\n\n        @Override\n        public String getDeclaringTypeName() {\n            return method.getDeclaringClass().getName();\n        }\n\n        @Override\n        public Class getReturnType() {\n            return method.getReturnType();\n        }\n\n        @Override\n        public Method getMethod() {\n            return method;\n        }\n\n        @Override\n        public Class[] getParameterTypes() {\n            return method.getParameterTypes();\n        }\n\n        @Override\n        public String[] getParameterNames() {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public Class[] getExceptionTypes() {\n            return method.getExceptionTypes();\n        }\n\n        @Override\n        public String toShortString() {\n            return toString(false, false, false, false);\n        }\n\n        @Override\n        public String toLongString() {\n            return toString(true, true, true, true);\n        }\n\n        @Override\n        public String toString() {\n            return toString(false, true, false, true);\n        }\n\n        private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,\n                                boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {\n            StringBuilder sb = new StringBuilder();\n            if (includeModifier) {\n                sb.append(Modifier.toString(getModifiers()));\n                sb.append(\" \");\n            }\n            if (includeReturnTypeAndArgs) {\n                appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);\n                sb.append(\" \");\n            }\n            appendType(sb, getDeclaringType(), useLongTypeName);\n            sb.append(\".\");\n            sb.append(getMethod().getName());\n            sb.append(\"(\");\n            Class[] parametersTypes = getParameterTypes();\n            appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);\n            sb.append(\")\");\n            return sb.toString();\n        }\n\n        private void appendTypes(StringBuilder sb, Class<?>[] types,\n                                 boolean includeArgs, boolean useLongReturnAndArgumentTypeName) {\n            if (includeArgs) {\n                for (int size = types.length, i = 0; i < size; i++) {\n                    appendType(sb, types[i], useLongReturnAndArgumentTypeName);\n                    if (i < size - 1) {\n                        sb.append(\",\");\n                    }\n                }\n            } else {\n                if (types.length != 0) {\n                    sb.append(\"..\");\n                }\n            }\n        }\n\n        private void appendType(StringBuilder sb, Class<?> type, boolean useLongTypeName) {\n            if (type.isArray()) {\n                appendType(sb, type.getComponentType(), useLongTypeName);\n                sb.append(\"[]\");\n            } else {\n                sb.append(useLongTypeName ? type.getName() : type.getSimpleName());\n            }\n        }\n    }\n\n\n    /**\n     * Lazily initialized SourceLocation.\n     */\n    private class SourceLocationImpl implements SourceLocation {\n\n        @Override\n        public Class getWithinType() {\n            if (proxy == null) {\n                throw new UnsupportedOperationException(\"No source location joinpoint available: target is null\");\n            }\n            return proxy.getClass();\n        }\n\n        @Override\n        public String getFileName() {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public int getLine() {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public int getColumn() {\n            throw new UnsupportedOperationException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/jdk/TylooInvokerInvocationHandler.java",
    "content": "package io.tyloo.dubbo.proxy.jdk;\n\nimport io.tyloo.api.Propagation;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.dubbo.context.DubboTransactionContextEditor;\nimport io.tyloo.interceptor.TylooCoordinatorAspect;\nimport io.tyloo.support.FactoryBuilder;\nimport io.tyloo.utils.ReflectionUtils;\nimport org.apache.dubbo.common.utils.StringUtils;\nimport org.apache.dubbo.rpc.Invoker;\nimport org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;\nimport org.aspectj.lang.ProceedingJoinPoint;\n\nimport java.lang.reflect.Method;\n\n/*\n *\n * TCC ô\n * ڵ Dubbo Service ʱʹ TylooCoordinatorAspect ش\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 10:55 2019/9/23\n *\n */\n\npublic class TylooInvokerInvocationHandler extends InvokerInvocationHandler {\n\n    private Object target;\n\n    public TylooInvokerInvocationHandler(Invoker<?> handler) {\n        super(handler);\n    }\n\n    public <T> TylooInvokerInvocationHandler(T target, Invoker<T> invoker) {\n        super(invoker);\n        this.target = target;\n    }\n\n    @Override\n    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\n        Tyloo tyloo = method.getAnnotation(Tyloo.class);\n\n        if (tyloo != null) {\n\n            if (StringUtils.isEmpty(tyloo.confirmMethod())) {\n                ReflectionUtils.changeAnnotationValue(tyloo, \"confirmMethod\", method.getName());\n                ReflectionUtils.changeAnnotationValue(tyloo, \"cancelMethod\", method.getName());\n                ReflectionUtils.changeAnnotationValue(tyloo, \"transactionContextEditor\", DubboTransactionContextEditor.class);\n                ReflectionUtils.changeAnnotationValue(tyloo, \"propagation\", Propagation.SUPPORTS);\n            }\n\n            /**\n             * ɷ\n             *  TylooCoordinatorAspect#interceptTransactionContextMethod Էش\n             * Ϊʲô TylooTransactionAspect 棿\n             * ΪΪ Propagation.SUPPORTSᷢ\n             */\n            ProceedingJoinPoint pjp = new MethodProceedingJoinPoint(proxy, target, method, args);\n            return FactoryBuilder.factoryOf(TylooCoordinatorAspect.class).getInstance().interceptTransactionContextMethod(pjp);\n        } else {\n            return super.invoke(target, method, args);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/jdk/TylooJdkProxyFactory.java",
    "content": "package io.tyloo.dubbo.proxy.jdk;\n\nimport org.apache.dubbo.rpc.Invoker;\nimport org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;\nimport org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory;\n\nimport java.lang.reflect.Proxy;\n\n\n/*\n *\n * TCC JDK \n *  JDK ̬\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 10:52 2019/9/29\n *\n */\npublic class TylooJdkProxyFactory extends JdkProxyFactory {\n    /**\n     * - Ŀʱ `TccJavassistProxyFactory#getProxy(...)`  Dubbo Service  Proxy\n     * - һε `Proxy#newProxyInstance(...)`  Dubbo Service  Proxy\n     * - ڶε `Proxy#newProxyInstance(...)` Ե Dubbo Service  Proxy  Proxy\n     *\n     * @param invoker\n     * @param interfaces\n     * @param <T>\n     * @return\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {\n\n        T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));\n\n        T tccProxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new TylooInvokerInvocationHandler(proxy, invoker));\n\n        return tccProxy;\n    }\n}"
  },
  {
    "path": "tyloo-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.ProxyFactory",
    "content": "tylooJavassist=TylooJavassistProxyFactory\ntylooJdk=TylooJdkProxyFactory"
  },
  {
    "path": "tyloo-dubbo/src/main/resources/tyloo-dubbo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\">\n\n       <dubbo:provider proxy=\"tylooJavassist\"/>\n    <aop:aspectj-autoproxy/>\n</beans>"
  },
  {
    "path": "tyloo-spring/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-spring</artifactId>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n\n\n\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aop</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context-support</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.quartz-scheduler</groupId>\n            <artifactId>quartz</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.12</version>\n        </dependency>\n\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-spring/src/main/dbscripts/db.sql",
    "content": "CREATE TABLE `TCC_TRANSACTION` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nALTER TABLE `TCC_TRANSACTION` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/ConfigurableCoordinatorAspect.java",
    "content": "package io.tyloo.spring;\n\nimport io.tyloo.interceptor.TylooCoordinatorAspect;\nimport io.tyloo.interceptor.TylooCoordinatorInterceptor;\nimport io.tyloo.support.TransactionConfigurator;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.core.Ordered;\n\n/*\n *\n * õԴЭ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:21 2019/10/4\n *\n */\n\n@Aspect\npublic class ConfigurableCoordinatorAspect extends TylooCoordinatorAspect implements Ordered {\n\n    private TransactionConfigurator transactionConfigurator;\n\n    /**\n     * ʼ\n     * ע TylooTransactionManager\n     */\n    public void init() {\n\n        TylooCoordinatorInterceptor tylooCoordinatorInterceptor = new TylooCoordinatorInterceptor();\n        tylooCoordinatorInterceptor.setTransactionManager(transactionConfigurator.getTransactionManager());\n        this.setTylooCoordinatorInterceptor(tylooCoordinatorInterceptor);\n    }\n\n    @Override\n    public int getOrder() {\n        return Ordered.HIGHEST_PRECEDENCE + 1;\n    }\n\n    public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {\n        this.transactionConfigurator = transactionConfigurator;\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/ConfigurableTransactionAspect.java",
    "content": "package io.tyloo.spring;\n\nimport io.tyloo.TransactionManager;\nimport io.tyloo.interceptor.TylooTransactionAspect;\nimport io.tyloo.interceptor.TylooTransactionInterceptor;\nimport io.tyloo.support.TransactionConfigurator;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.core.Ordered;\n\n/*\n *\n * õĿɲ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:13 2019/10/7\n *\n */\n@Aspect\npublic class ConfigurableTransactionAspect extends TylooTransactionAspect implements Ordered {\n\n    private TransactionConfigurator transactionConfigurator;\n\n    /**\n     * ʼ\n     * ע DelayCancelExceptions  setTylooTransactionManager\n     */\n    public void init() {\n\n        TransactionManager transactionManager = transactionConfigurator.getTransactionManager();\n\n        TylooTransactionInterceptor tylooTransactionInterceptor = new TylooTransactionInterceptor();\n        tylooTransactionInterceptor.setTransactionManager(transactionManager);\n        tylooTransactionInterceptor.setDelayCancelExceptions(transactionConfigurator.getTylooRecoverConfiguration().getDelayCancelExceptions());\n\n        this.setTylooTransactionInterceptor(tylooTransactionInterceptor);\n    }\n\n    @Override\n    public int getOrder() {\n        return Ordered.HIGHEST_PRECEDENCE;\n    }\n\n    public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {\n        this.transactionConfigurator = transactionConfigurator;\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/recover/DefaultTylooRecoverConfiguration.java",
    "content": "package io.tyloo.spring.recover;\n\nimport io.tyloo.OptimisticLockException;\nimport io.tyloo.recover.TylooRecoverConfiguration;\nimport lombok.*;\n\nimport java.net.SocketTimeoutException;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/*\n *\n * Ĭָ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:14 2019/10/10\n *\n */\n\n@AllArgsConstructor\n@Data\n@Builder\npublic class DefaultTylooRecoverConfiguration implements TylooRecoverConfiguration {\n\n    public static final TylooRecoverConfiguration INSTANCE = new DefaultTylooRecoverConfiguration();\n\n    /**\n     * һೢԻָԶָҪ˹ԤĬ30Σ\n     */\n    private int maxRetryCount = 30;\n\n    /**\n     * һ־һʱûи¾ͻᱻΪǷ쳣Ҫָ\n     * ָJobɨ賬ʱûиµ־Щлָʱ䵥λ룬Ĭ120\n     */\n    private int recoverDuration = 120; //120 seconds\n\n    /**\n     * ָJobãĬ(ÿ)\n     * cron ʽ\n     * 0/30 * * * * ?ÿ 30 ִһΡ\n     */\n    private String cronExpression = \"0 */1 * * * ?\";\n\n    private int asyncTerminateThreadCorePoolSize = 512;\n\n    private int asyncTerminateThreadMaxPoolSize = 1024;\n\n    private int asyncTerminateThreadWorkQueueSize = 512;\n\n    /**\n     * ӳȡ쳣\n     */\n    private Set<Class<? extends Exception>> delayCancelExceptions = new HashSet<Class<? extends Exception>>();\n\n    public DefaultTylooRecoverConfiguration() {\n\n        /**\n         *  SocketTimeoutException ָʱС Socket ʱʱ䣬ʱָԶ̲ȡع\n         * Զ̲´θʱΪֹʧܣ׳ OptimisticLockException TylooInterceptor ʱȡع\n         * ܻͶʱȡعͻͳһʱ\n         *\n         */\n        delayCancelExceptions.add(OptimisticLockException.class);\n        /**\n         * try ׶ΣزߵԶ̲( Զ̷ DubboHttp )Զ̲ try ׶εķ߼ִʱϳ Socket ȴʱ SocketTimeoutException\n         * ִعԶ̲ try ķδִɣܵ cancel ķʵδִ( try ķδִɣݿ񡾷 TCC δύ\n         * cancel ķȡʱδ·ʵδִУ try ķִύݿ񡾷 TCC 񡿣Ϊ )ݲһ¡\n         * ָʱȡعʱԶ̲ߵ try ķδǿܷݲһ¡\n         *\n         */\n        delayCancelExceptions.add(SocketTimeoutException.class);\n    }\n\n\n    @Override\n    public void setDelayCancelExceptions(Set<Class<? extends Exception>> delayCancelExceptions) {\n        this.delayCancelExceptions.addAll(delayCancelExceptions);\n    }\n\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/recover/RecoverScheduledJob.java",
    "content": "package io.tyloo.spring.recover;\n\nimport io.tyloo.SystemException;\nimport io.tyloo.recover.TylooTransactionRecovery;\nimport io.tyloo.support.TransactionConfigurator;\nimport org.quartz.Scheduler;\nimport org.springframework.scheduling.quartz.CronTriggerFactoryBean;\nimport org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;\n\n/*\n *\n * ָʱ\n *  Quartz ʵֵȣִָ\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:15 2019/10/29\n *\n */\n\npublic class RecoverScheduledJob {\n\n    /**\n     * ָ\n     */\n    private TylooTransactionRecovery tylooTransactionRecovery;\n\n    /**\n     * עTCC.\n     */\n    private TransactionConfigurator transactionConfigurator;\n\n    /**\n     * ָ(עorg.springframework.scheduling.quartz.SchedulerFactoryBeanʵ)\n     */\n    private Scheduler scheduler;\n\n    /**\n     * ʼSpringʱִ.\n     */\n    public void init() {\n\n        try {\n            // MethodInvokingJobDetailFactoryBean ɾֻҪָĳĳڴʱָָ\n            MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();\n\n            // ָӦĵöʵκνӿ\n            jobDetail.setTargetObject(tylooTransactionRecovery);\n\n            // ָtargetObjectĳķ(˴TransactionRecoveryеstartRecover)\n            jobDetail.setTargetMethod(\"startRecover\");\n\n            // \n            jobDetail.setName(\"transactionRecoveryJob\");\n\n            // Ƿ񲢷ִУĬǲִеģʱáconcurrentΪfalseܴܿ⣬ҼʽС׸,\n            // Ϊfalseʾһִٿµ\n            jobDetail.setConcurrent(false);\n            jobDetail.afterPropertiesSet();\n\n            // ࣬ñָĵָĴ\n            // ฺspringдһIDӦSchedulerFactoryBeanԵListбãܱ֤ĳָ\n            CronTriggerFactoryBean cronTrigger = new CronTriggerFactoryBean();\n\n            // ô\n            cronTrigger.setBeanName(\"transactionRecoveryCronTrigger\");\n            // ͨȡָʱ\n            cronTrigger.setCronExpression(transactionConfigurator.getTylooRecoverConfiguration().getCronExpression());\n            cronTrigger.setJobDetail(jobDetail.getObject());\n            cronTrigger.afterPropertiesSet();\n\n            // õ\n            scheduler.scheduleJob(jobDetail.getObject(), cronTrigger.getObject());\n\n            // \n            scheduler.start();\n\n        } catch (Exception e) {\n            throw new SystemException(e);\n        }\n    }\n\n    public void setTylooTransactionRecovery(TylooTransactionRecovery tylooTransactionRecovery) {\n        this.tylooTransactionRecovery = tylooTransactionRecovery;\n    }\n\n    public Scheduler getScheduler() {\n        return scheduler;\n    }\n\n    public void setScheduler(Scheduler scheduler) {\n        this.scheduler = scheduler;\n    }\n\n    public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {\n        this.transactionConfigurator = transactionConfigurator;\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/repository/SpringJdbcTransactionRepository.java",
    "content": "package io.tyloo.spring.repository;\n\n\nimport io.tyloo.repository.JdbcTransactionRepository;\nimport org.springframework.jdbc.datasource.DataSourceUtils;\n\nimport java.sql.Connection;\n\n/*\n *\n * SpringJdbc\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:18 2019/10/30\n *\n */\n\npublic class SpringJdbcTransactionRepository extends JdbcTransactionRepository {\n\n    @Override\n    protected Connection getConnection() {\n        return DataSourceUtils.getConnection(this.getDataSource());\n    }\n\n    @Override\n    protected void releaseConnection(Connection con) {\n        DataSourceUtils.releaseConnection(con, this.getDataSource());\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/support/SpringBeanFactory.java",
    "content": "package io.tyloo.spring.support;\n\nimport io.tyloo.support.BeanFactory;\nimport io.tyloo.support.FactoryBuilder;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\n\nimport java.util.Map;\n\n/*\n *\n * Spring Bean \n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:12 2019/11/11\n *\n */\n\npublic class SpringBeanFactory implements BeanFactory, ApplicationContextAware {\n\n    private ApplicationContext applicationContext;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n        FactoryBuilder.registerBeanFactory(this);\n    }\n\n    @Override\n    public boolean isFactoryOf(Class clazz) {\n        Map map = this.applicationContext.getBeansOfType(clazz);\n        return map.size() > 0;\n    }\n\n    @Override\n    public <T> T getBean(Class<T> var1) {\n        return this.applicationContext.getBean(var1);\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/support/SpringPostProcessor.java",
    "content": "package io.tyloo.spring.support;\n\nimport io.tyloo.support.BeanFactory;\nimport io.tyloo.support.FactoryBuilder;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextRefreshedEvent;\n\n/*\n *\n * Spring ô\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:13 2019/11/18\n *\n */\n\npublic class SpringPostProcessor implements ApplicationListener<ContextRefreshedEvent> {\n    /**\n     * Springʱ.\n     */\n    @Override\n    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {\n        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();\n\n        if (applicationContext.getParent() == null) {\n            FactoryBuilder.registerBeanFactory(applicationContext.getBean(BeanFactory.class));\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/java/io/tyloo/spring/support/SpringTransactionConfigurator.java",
    "content": "package io.tyloo.spring.support;\n\nimport io.tyloo.TransactionManager;\nimport io.tyloo.TransactionRepository;\nimport io.tyloo.recover.TylooRecoverConfiguration;\nimport io.tyloo.repository.CachableTransactionRepository;\nimport io.tyloo.spring.recover.DefaultTylooRecoverConfiguration;\nimport io.tyloo.support.TransactionConfigurator;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.concurrent.*;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/*\n *\n * Spring\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 20:20 2019/12/4\n *\n */\npublic class SpringTransactionConfigurator implements TransactionConfigurator {\n\n    private static volatile ExecutorService executorService = null;\n\n    @Autowired\n    private TransactionRepository transactionRepository;\n\n    @Autowired(required = false)\n    private TylooRecoverConfiguration tylooRecoverConfiguration = DefaultTylooRecoverConfiguration.INSTANCE;\n\n\n    private TransactionManager transactionManager;\n\n    public void init() {\n        transactionManager = new TransactionManager();\n        transactionManager.setTransactionRepository(transactionRepository);\n\n        if (executorService == null) {\n            Executors.defaultThreadFactory();\n            synchronized (SpringTransactionConfigurator.class) {\n                if (executorService == null) {\n                    executorService = new ThreadPoolExecutor(\n                            tylooRecoverConfiguration.getAsyncTerminateThreadCorePoolSize(),\n                            tylooRecoverConfiguration.getAsyncTerminateThreadMaxPoolSize(),\n                            5L,\n                            TimeUnit.SECONDS,\n                            new ArrayBlockingQueue<>(tylooRecoverConfiguration.getAsyncTerminateThreadWorkQueueSize()),\n                            new ThreadFactory() {\n\n                                final AtomicInteger poolNumber = new AtomicInteger(1);\n                                final ThreadGroup group;\n                                final AtomicInteger threadNumber = new AtomicInteger(1);\n                                final String namePrefix;\n\n                                {\n                                    SecurityManager securityManager = System.getSecurityManager();\n                                    this.group = securityManager != null ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();\n                                    this.namePrefix = \"tcc-async-terminate-pool-\" + poolNumber.getAndIncrement() + \"-thread-\";\n                                }\n\n                                @Override\n                                public Thread newThread(Runnable runnable) {\n                                    Thread thread = new Thread(this.group, runnable, this.namePrefix + this.threadNumber.getAndIncrement(), 0L);\n                                    if (thread.isDaemon()) {\n                                        thread.setDaemon(false);\n                                    }\n\n                                    if (thread.getPriority() != 5) {\n                                        thread.setPriority(5);\n                                    }\n\n                                    return thread;\n                                }\n                            },\n                            new ThreadPoolExecutor.CallerRunsPolicy());\n                }\n            }\n        }\n\n        transactionManager.setExecutorService(executorService);\n\n        if (transactionRepository instanceof CachableTransactionRepository) {\n            ((CachableTransactionRepository) transactionRepository).setExpireDuration(tylooRecoverConfiguration.getRecoverDuration());\n        }\n    }\n\n    @Override\n    public TransactionManager getTransactionManager() {\n        return transactionManager;\n    }\n\n    @Override\n    public TransactionRepository getTransactionRepository() {\n        return transactionRepository;\n    }\n\n    @Override\n    public TylooRecoverConfiguration getTylooRecoverConfiguration() {\n        return tylooRecoverConfiguration;\n    }\n}\n"
  },
  {
    "path": "tyloo-spring/src/main/resources/tyloo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:task=\"http://www.springframework.org/schema/task\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd\">\n\n    <aop:aspectj-autoproxy proxy-target-class=\"true\"/>\n\n    <bean id=\"springBeanFactory\" class=\"io.tyloo.spring.support.SpringBeanFactory\"/>\n\n    <bean id=\"transactionConfigurator\" class=\"io.tyloo.spring.support.SpringTransactionConfigurator\"\n          init-method=\"init\"/>\n\n    <bean id=\"tylooTransactionAspect\" class=\"io.tyloo.spring.ConfigurableTransactionAspect\"\n          init-method=\"init\">\n        <property name=\"transactionConfigurator\" ref=\"transactionConfigurator\"/>\n    </bean>\n\n    <bean id=\"tylooCoordinatorAspect\" class=\"io.tyloo.spring.ConfigurableCoordinatorAspect\"\n          init-method=\"init\">\n        <property name=\"transactionConfigurator\" ref=\"transactionConfigurator\"/>\n    </bean>\n\n    <task:annotation-driven/>\n\n\n    <bean id=\"transactionRecovery\" class=\"io.tyloo.recover.TylooTransactionRecovery\">\n        <property name=\"transactionConfigurator\" ref=\"transactionConfigurator\"/>\n    </bean>\n\n    <bean id=\"recoverScheduler\" class=\"org.springframework.scheduling.quartz.SchedulerFactoryBean\"/>\n\n    <bean id=\"recoverScheduledJob\" class=\"io.tyloo.spring.recover.RecoverScheduledJob\"\n          init-method=\"init\">\n        <property name=\"tylooTransactionRecovery\" ref=\"transactionRecovery\"/>\n        <property name=\"transactionConfigurator\" ref=\"transactionConfigurator\"/>\n        <property name=\"scheduler\" ref=\"recoverScheduler\"/>\n    </bean>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-tutorial-sample</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>tyloo-dubbo-sample</module>\n        <module>tyloo-http-sample</module>\n        <module>tyloo-server-sample</module>\n        <module>tyloo-sample-domain</module>\n    </modules>\n\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/src/tylooSampledb/create_db_cap.sql",
    "content": "CREATE DATABASE `TCC_CAP` /*!40100 DEFAULT CHARACTER SET utf8 */;\nuse TCC_CAP;\nCREATE TABLE `CAP_CAPITAL_ACCOUNT` (\n  `CAPITAL_ACCOUNT_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `USER_ID` int(11) DEFAULT NULL,\n  PRIMARY KEY (`CAPITAL_ACCOUNT_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `CAP_TRADE_ORDER` (\n  `ID` int(11) NOT NULL AUTO_INCREMENT,\n  `SELF_USER_ID` bigint(11) DEFAULT NULL,\n  `OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,\n  `MERCHANT_ORDER_NO` varchar(45) NOT NULL,\n  `AMOUNT` decimal(10,0) DEFAULT NULL,\n  `STATUS` varchar(45) DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `UX_MERCHANT_ORDER_NO` (`MERCHANT_ORDER_NO`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nINSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (1,10000,1000);\nINSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (2,10000,2000);"
  },
  {
    "path": "tyloo-tutorial-sample/src/tylooSampledb/create_db_ord.sql",
    "content": "CREATE DATABASE `TCC_ORD` /*!40100 DEFAULT CHARACTER SET utf8 */;\nuse TCC_ORD;\nCREATE TABLE `ORD_ORDER` (\n  `ORDER_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `PAYER_USER_ID` int(11) DEFAULT NULL,\n  `PAYEE_USER_ID` int(11) DEFAULT NULL,\n  `RED_PACKET_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `CAPITAL_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `STATUS` varchar(45) DEFAULT NULL,\n  `MERCHANT_ORDER_NO` varchar(45) NOT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`ORDER_ID`),\n  UNIQUE KEY `MERCHANT_ORDER_NO_UNIQUE` (`MERCHANT_ORDER_NO`)\n) ENGINE=InnoDB AUTO_INCREMENT=1188 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `ORD_ORDER_LINE` (\n  `ORDER_LINE_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `PRODUCT_ID` int(11) DEFAULT NULL,\n  `QUANTITY` decimal(10,0) DEFAULT NULL,\n  `UNIT_PRICE` decimal(10,0) DEFAULT NULL,\n  PRIMARY KEY (`ORDER_LINE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `ORD_SHOP` (\n  `SHOP_ID` int(11) NOT NULL,\n  `OWNER_USER_ID` int(11) DEFAULT NULL,\n  PRIMARY KEY (`SHOP_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `ORD_PRODUCT`(\n  `PRODUCT_ID` int(11) NOT NULL,\n  `SHOP_ID` int(11) NOT NULL,\n  `PRODUCT_NAME` VARCHAR(64) DEFAULT NULL ,\n  `PRICE` DECIMAL(10,0) DEFAULT NULL,\n  PRIMARY KEY (`PRODUCT_ID`)\n)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\n\nINSERT INTO `ORD_SHOP` (`SHOP_ID`,`OWNER_USER_ID`) VALUES (1,1000);\n\nINSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (1,1,'IPhone6S',5288);\nINSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (2,1,'MAC Pro',10288);\nINSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (3,1,'IWatch',2288);"
  },
  {
    "path": "tyloo-tutorial-sample/src/tylooSampledb/create_db_red.sql",
    "content": "CREATE DATABASE `TCC_RED` /*!40100 DEFAULT CHARACTER SET utf8 */;\nuse TCC_RED;\nCREATE TABLE `RED_RED_PACKET_ACCOUNT` (\n  `RED_PACKET_ACCOUNT_ID` int(11) NOT NULL,\n  `BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `USER_ID` int(11) DEFAULT NULL,\n  PRIMARY KEY (`RED_PACKET_ACCOUNT_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `RED_TRADE_ORDER` (\n  `ID` int(11) NOT NULL AUTO_INCREMENT,\n  `SELF_USER_ID` bigint(11) DEFAULT NULL,\n  `OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,\n  `MERCHANT_ORDER_NO` varchar(45) NOT NULL,\n  `AMOUNT` decimal(10,0) DEFAULT NULL,\n  `STATUS` varchar(45) DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `MERCHANT_ORDER_NO_UNIQUE` (`MERCHANT_ORDER_NO`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nINSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (1,950,1000);\nINSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (2,500,2000);\n"
  },
  {
    "path": "tyloo-tutorial-sample/src/tylooSampledb/create_db_tcc.sql",
    "content": "CREATE DATABASE `TCC` /*!40100 DEFAULT CHARACTER SET utf8 */;\nuse TCC;\nCREATE TABLE `TCC_TRANSACTION_CAP` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nALTER TABLE `TCC_TRANSACTION_CAP` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;\n\nCREATE TABLE `TCC_TRANSACTION_ORD` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nALTER TABLE `TCC_TRANSACTION_ORD` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;\n\nCREATE TABLE `TCC_TRANSACTION_RED` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nALTER TABLE `TCC_TRANSACTION_RED` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;\n\nCREATE TABLE `TCC_TRANSACTION_UT` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nALTER TABLE `TCC_TRANSACTION_UT` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-tutorial-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo-sample</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>tyloo-dubbo-capital</module>\n        <module>tyloo-dubbo-capital-api</module>\n        <module>tyloo-dubbo-order</module>\n        <module>tyloo-dubbo-redpacket</module>\n        <module>tyloo-dubbo-redpacket-api</module>\n    </modules>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_cap.sql",
    "content": "CREATE DATABASE `tcc_cap` /*!40100 DEFAULT CHARACTER SET utf8 */;\n\nuse tcc_cap;\n\nCREATE TABLE `CAP_CAPITAL_ACCOUNT` (\n  `CAPITAL_ACCOUNT_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `USER_ID` int(11) DEFAULT NULL,\n  PRIMARY KEY (`CAPITAL_ACCOUNT_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `CAP_TRADE_ORDER` (\n  `ID` int(11) NOT NULL AUTO_INCREMENT,\n  `SELF_USER_ID` bigint(11) DEFAULT NULL,\n  `OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,\n  `MERCHANT_ORDER_NO` varchar(45) DEFAULT NULL,\n  `AMOUNT` decimal(10,0) DEFAULT NULL,\n  `STATUS` varchar(45) DEFAULT NULL,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nINSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (1,10000,1000);\nINSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (2,10000,2000);"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_ord.sql",
    "content": "CREATE DATABASE `TCC_ORD` /*!40100 DEFAULT CHARACTER SET utf8 */;\n\nuse TCC_ORD;\n\nCREATE TABLE `ORD_ORDER` (\n  `ORDER_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `PAYER_USER_ID` int(11) DEFAULT NULL,\n  `PAYEE_USER_ID` int(11) DEFAULT NULL,\n  `RED_PACKET_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `CAPITAL_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `STATUS` varchar(45) DEFAULT NULL,\n  `MERCHANT_ORDER_NO` varchar(45) DEFAULT NULL,\n  PRIMARY KEY (`ORDER_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `ORD_ORDER_LINE` (\n  `ORDER_LINE_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `PRODUCT_ID` int(11) DEFAULT NULL,\n  `QUANTITY` decimal(10,0) DEFAULT NULL,\n  `UNIT_PRICE` decimal(10,0) DEFAULT NULL,\n  PRIMARY KEY (`ORDER_LINE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `ORD_SHOP` (\n  `SHOP_ID` int(11) NOT NULL,\n  `OWNER_USER_ID` int(11) DEFAULT NULL,\n  PRIMARY KEY (`SHOP_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `ORD_PRODUCT`(\n  `PRODUCT_ID` int(11) NOT NULL,\n  `SHOP_ID` int(11) NOT NULL,\n  `PRODUCT_NAME` VARCHAR(64) DEFAULT NULL ,\n  `PRICE` DECIMAL(10,0) DEFAULT NULL,\n  PRIMARY KEY (`PRODUCT_ID`)\n)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\n\nINSERT INTO `ORD_SHOP` (`SHOP_ID`,`OWNER_USER_ID`) VALUES (1,1000);\n\nINSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (1,1,'IPhone6S',5288);\nINSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (2,1,'MAC Pro',10288);\nINSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (3,1,'IWatch',2288);"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_red.sql",
    "content": "CREATE DATABASE `tcc_red` /*!40100 DEFAULT CHARACTER SET utf8 */;\nuse tcc_red;\nCREATE TABLE `RED_RED_PACKET_ACCOUNT` (\n  `RED_PACKET_ACCOUNT_ID` int(11) NOT NULL,\n  `BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,\n  `USER_ID` int(11) DEFAULT NULL,\n  PRIMARY KEY (`RED_PACKET_ACCOUNT_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `RED_TRADE_ORDER` (\n  `ID` int(11) NOT NULL AUTO_INCREMENT,\n  `SELF_USER_ID` BIGINT(11) DEFAULT NULL,\n  `OPPOSITE_USER_ID` BIGINT(11) DEFAULT NULL,\n  `MERCHANT_ORDER_NO` varchar(45) DEFAULT NULL,\n  `AMOUNT` decimal(10,0) DEFAULT NULL,\n  `STATUS` varchar(45) DEFAULT NULL,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nINSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (1,950,1000);\nINSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (2,500,2000);\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_tcc.sql",
    "content": "CREATE DATABASE `TCC` /*!40100 DEFAULT CHARACTER SET utf8 */;\n\nuse TCC;\n\nCREATE TABLE `TCC_TRANSACTION_CAP` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `TCC_TRANSACTION_ORD` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `TCC_TRANSACTION_RED` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\nCREATE TABLE `TCC_TRANSACTION_UT` (\n  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,\n  `DOMAIN` varchar(100) DEFAULT NULL,\n  `GLOBAL_TX_ID` varbinary(32) NOT NULL,\n  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,\n  `CONTENT` varbinary(8000) DEFAULT NULL,\n  `STATUS` int(11) DEFAULT NULL,\n  `TRANSACTION_TYPE` int(11) DEFAULT NULL,\n  `RETRIED_COUNT` int(11) DEFAULT NULL,\n  `CREATE_TIME` datetime DEFAULT NULL,\n  `LAST_UPDATE_TIME` datetime DEFAULT NULL,\n  `VERSION` int(11) DEFAULT NULL,\n  PRIMARY KEY (`TRANSACTION_ID`),\n  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-dubbo-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo-capital</artifactId>\n    <packaging>war</packaging>\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo-capital-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-captial</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo</artifactId>\n           <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <!--<dependency>-->\n            <!--<groupId>com.101tec</groupId>-->\n            <!--<artifactId>zkclient</artifactId>-->\n        <!--</dependency>-->\n\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webmvc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/java/io/tyloo/sample/dubbo/capital/service/CapitalAccountServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.capital.service;\n\nimport io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;\nimport io.tyloo.sample.dubbo.capital.api.CapitalAccountService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\n\n@Service(\"capitalAccountService\")\npublic class CapitalAccountServiceImpl implements CapitalAccountService {\n\n\n    @Autowired\n    CapitalAccountRepository capitalAccountRepository;\n\n    @Override\n    public BigDecimal getCapitalAccountByUserId(long userId) {\n        return capitalAccountRepository.findByUserId(userId).getBalanceAmount();\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/java/io/tyloo/sample/dubbo/capital/service/CapitalTradeOrderServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.capital.service;\n\nimport io.tyloo.sample.capital.domain.entity.CapitalAccount;\nimport io.tyloo.sample.capital.domain.entity.TradeOrder;\nimport io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;\nimport io.tyloo.sample.capital.domain.repository.TradeOrderRepository;\nimport io.tyloo.sample.dubbo.capital.api.CapitalTradeOrderService;\nimport io.tyloo.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.dubbo.context.DubboTransactionContextEditor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataIntegrityViolationException;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Calendar;\n\n\n@Service(\"capitalTradeOrderService\")\npublic class CapitalTradeOrderServiceImpl implements CapitalTradeOrderService {\n\n    @Autowired\n    CapitalAccountRepository capitalAccountRepository;\n\n    @Autowired\n    TradeOrderRepository tradeOrderRepository;\n\n    @Override\n    @Tyloo(confirmMethod = \"confirmRecord\", cancelMethod = \"cancelRecord\", transactionContextEditor = DubboTransactionContextEditor.class)\n    @Transactional\n    public String record(CapitalTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000L);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"capital try record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n\n        TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n\n        //check if trade order has been recorded, if yes, return success directly.\n        if (foundTradeOrder == null) {\n\n            TradeOrder tradeOrder = new TradeOrder(\n                    tradeOrderDto.getSelfUserId(),\n                    tradeOrderDto.getOppositeUserId(),\n                    tradeOrderDto.getMerchantOrderNo(),\n                    tradeOrderDto.getAmount()\n            );\n\n            try {\n                tradeOrderRepository.insert(tradeOrder);\n\n                CapitalAccount transferFromAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n                transferFromAccount.transferFrom(tradeOrderDto.getAmount());\n\n                capitalAccountRepository.save(transferFromAccount);\n\n            } catch (DataIntegrityViolationException e) {\n                //this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.\n            }\n        }\n\n        return \"success\";\n    }\n\n    @Transactional\n    public void confirmRecord(CapitalTradeOrderDto tradeOrderDto) {\n        try {\n            Thread.sleep(1000L);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n        System.out.println(\"capital confirm record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.\n        if (tradeOrder != null && tradeOrder.getStatus().equals(\"DRAFT\")) {\n            tradeOrder.confirm();\n            tradeOrderRepository.update(tradeOrder);\n\n            CapitalAccount transferToAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());\n\n            transferToAccount.transferTo(tradeOrderDto.getAmount());\n\n            capitalAccountRepository.save(transferToAccount);\n        }\n    }\n\n    @Transactional\n    public void cancelRecord(CapitalTradeOrderDto tradeOrderDto) {\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"capital cancel record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.\n        if (null != tradeOrder && \"DRAFT\".equals(tradeOrder.getStatus())) {\n            tradeOrder.cancel();\n            tradeOrderRepository.update(tradeOrder);\n\n            CapitalAccount capitalAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n            capitalAccount.cancelTransfer(tradeOrderDto.getAmount());\n\n            capitalAccountRepository.save(capitalAccount);\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/config/spring/local/appcontext-service-provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <dubbo:application name=\"sample-dubbo-capital\"/>\n\n    <context:property-placeholder location=\"classpath:sample-dubbo-capital.properties\"\n                                  ignore-unresolvable=\"true\"/>\n\n    <dubbo:registry id=\"capitalRpcZk\" protocol=\"zookeeper\" address=\"${zookeeper.address}\"\n                    session=\"${zookeeper.session.timeout}\" timeout=\"${zookeeper.connect.timeout}\"/>\n\n    <dubbo:protocol name=\"dubbo\" port=\"${dubbo.port}\"/>\n\n    <dubbo:service interface=\"io.tyloo.sample.dubbo.capital.api.CapitalTradeOrderService\"\n                   registry=\"capitalRpcZk\"\n                   ref=\"capitalTradeOrderService\" timeout=\"60000\" retries=\"0\"/>\n    <dubbo:service interface=\"io.tyloo.sample.dubbo.capital.api.CapitalAccountService\"\n                   registry=\"capitalRpcZk\"\n                   ref=\"capitalAccountService\" timeout=\"60000\" retries=\"0\"/>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/config/spring/local/appcontext-service-tcc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample.dubbo\"/>\n\n    <util:properties id=\"tccJdbc\" location=\"classpath:tccjdbc.properties\"/>\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"30\"/>\n        <property name=\"recoverDuration\" value=\"60\"/>\n        <property name=\"cronExpression\" value=\"0/30 * * * * ?\"/>\n    </bean>\n\n    <bean id=\"transactionRepository\" class=\"io.tyloo.spring.repository.SpringJdbcTransactionRepository\">\n        <property name=\"dataSource\" ref=\"tylooTccDataSource\"/>\n        <property name=\"domain\" value=\"CAPITAL\"/>\n        <property name=\"tbSuffix\" value=\"_CAP\"/>\n    </bean>\n\n    <bean id=\"tylooTccDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{tccJdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{tccJdbc['tcc.jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{tccJdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{tccJdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{tccJdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{tccJdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{tccJdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{tccJdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{tccJdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{tccJdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <!--<bean id=\"transactionRepository\" class=\"RedisTransactionRepository\">-->\n    <!--<property name=\"keyPrefix\" value=\"TCC:CAP:\"/>-->\n    <!--<property name=\"jedisPool\" ref=\"jedisPool\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">-->\n    <!--<property name=\"maxTotal\" value=\"1000\"/>-->\n    <!--<property name=\"maxWaitMillis\" value=\"1000\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">-->\n    <!--<constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>-->\n    <!--<constructor-arg index=\"1\" value=\"127.0.0.1\"/>-->\n    <!--<constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>-->\n    <!--<constructor-arg index=\"3\" value=\"1000\" type=\"int\"/>-->\n    <!--<constructor-arg index=\"4\" type=\"java.lang.String\">-->\n    <!--<null/>-->\n    <!--</constructor-arg>-->\n    <!--<constructor-arg index=\"5\" value=\"0\" type=\"int\"/>-->\n    <!--</bean>-->\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/config/spring/local/appcontext-web-servlet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-dubbo-capital]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/sample-dubbo-capital.properties",
    "content": "dubbo.port=2881\n\nzookeeper.address=127.0.0.1:2181\nzookeeper.session.timeout=8000\nzookeeper.connect.timeout=2000"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/tccjdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=30000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/webapp/WEB-INF/web.xml",
    "content": "<!DOCTYPE web-app PUBLIC\n        \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\n        \"http://java.sun.com/dtd/web-app_2_3.dtd\" >\n\n<web-app>\n    <display-name>Sample Dubbo Capital</display-name>\n\n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml,classpath:tyloo-dubbo.xml\n        </param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jConfigLocation</param-name>\n        <param-value>classpath:log/log4j.xml</param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jRefreshInterval</param-name>\n        <param-value>60000</param-value>\n    </context-param>\n\n    <listener>\n        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n    </listener>\n\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n\n    <filter>\n        <filter-name>characterEncodingFilter</filter-name>\n        <display-name>Character Encoding Filter</display-name>\n        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>\n        <init-param>\n            <param-name>encoding</param-name>\n            <param-value>UTF-8</param-value>\n        </init-param>\n        <init-param>\n            <param-name>forceEncoding</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>characterEncodingFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n    </filter-mapping>\n\n    <servlet>\n        <servlet-name>springmvc</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>classpath:config/spring/local/appcontext-web-servlet.xml</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>springmvc</servlet-name>\n        <url-pattern>/</url-pattern>\n    </servlet-mapping>\n\n</web-app>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/webapp/index.jsp",
    "content": "hello tcc transacton dubbo sample capital"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-dubbo-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo-capital-api</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/src/main/java/io/tyloo/sample/dubbo/capital/api/CapitalAccountService.java",
    "content": "package io.tyloo.sample.dubbo.capital.api;\n\nimport java.math.BigDecimal;\n\n\npublic interface CapitalAccountService {\n\n    BigDecimal getCapitalAccountByUserId(long userId);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/src/main/java/io/tyloo/sample/dubbo/capital/api/CapitalTradeOrderService.java",
    "content": "package io.tyloo.sample.dubbo.capital.api;\n\nimport io.tyloo.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;\nimport io.tyloo.api.Tyloo;\n\n\npublic interface CapitalTradeOrderService {\n\n    @Tyloo\n    public String record(CapitalTradeOrderDto tradeOrderDto);\n\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/src/main/java/io/tyloo/sample/dubbo/capital/api/dto/CapitalTradeOrderDto.java",
    "content": "package io.tyloo.sample.dubbo.capital.api.dto;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\npublic class CapitalTradeOrderDto implements Serializable {\n\n    private static final long serialVersionUID = 6627401903410124642L;\n    \n    private long selfUserId;\n\n    private long oppositeUserId;\n\n    private String orderTitle;\n\n    private String merchantOrderNo;\n\n    private BigDecimal amount;\n\n    public long getSelfUserId() {\n        return selfUserId;\n    }\n\n    public void setSelfUserId(long selfUserId) {\n        this.selfUserId = selfUserId;\n    }\n\n    public long getOppositeUserId() {\n        return oppositeUserId;\n    }\n\n    public void setOppositeUserId(long oppositeUserId) {\n        this.oppositeUserId = oppositeUserId;\n    }\n\n    public String getOrderTitle() {\n        return orderTitle;\n    }\n\n    public void setOrderTitle(String orderTitle) {\n        this.orderTitle = orderTitle;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public void setMerchantOrderNo(String merchantOrderNo) {\n        this.merchantOrderNo = merchantOrderNo;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-dubbo-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo-order</artifactId>\n    <packaging>war</packaging>\n\n    <dependencies>\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo</artifactId>\n           <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo-capital-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo-redpacket-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-order</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <!--<dependency>-->\n            <!--<groupId>com.101tec</groupId>-->\n            <!--<artifactId>zkclient</artifactId>-->\n        <!--</dependency>-->\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webmvc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.freemarker</groupId>\n            <artifactId>freemarker</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/service/AccountServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.order.service;\n\nimport io.tyloo.sample.dubbo.capital.api.CapitalAccountService;\nimport io.tyloo.sample.dubbo.redpacket.api.RedPacketAccountService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\n\n\n@Service(\"accountService\")\npublic class AccountServiceImpl {\n\n    @Autowired\n    RedPacketAccountService redPacketAccountService;\n\n    @Autowired\n    CapitalAccountService capitalAccountService;\n\n\n    public BigDecimal getRedPacketAccountByUserId(long userId){\n        return redPacketAccountService.getRedPacketAccountByUserId(userId);\n    }\n\n    public BigDecimal getCapitalAccountByUserId(long userId){\n        return capitalAccountService.getCapitalAccountByUserId(userId);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/service/PaymentServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.order.service;\n\nimport io.tyloo.sample.dubbo.capital.api.CapitalTradeOrderService;\nimport io.tyloo.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;\nimport io.tyloo.sample.dubbo.redpacket.api.RedPacketTradeOrderService;\nimport io.tyloo.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.repository.OrderRepository;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.UniqueIdentity;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.OptimisticLockingFailureException;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\nimport java.net.SocketTimeoutException;\nimport java.util.Calendar;\n\n\n@Service\npublic class PaymentServiceImpl {\n\n    @Autowired\n    CapitalTradeOrderService capitalTradeOrderService;\n\n    @Autowired\n    RedPacketTradeOrderService redPacketTradeOrderService;\n\n    @Autowired\n    OrderRepository orderRepository;\n\n    @Tyloo(confirmMethod = \"confirmMakePayment\", cancelMethod = \"cancelMakePayment\", asyncConfirm = false, delayCancelExceptions = {SocketTimeoutException.class, org.apache.dubbo.remoting.TimeoutException.class})\n    public void makePayment(@UniqueIdentity String orderNo, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n        System.out.println(\"order try make payment called.time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        Order order = orderRepository.findByMerchantOrderNo(orderNo);\n        //check if the order status is DRAFT, if no, means that another call makePayment for the same order happened, ignore this call makePayment.\n        if (order.getStatus().equals(\"DRAFT\")) {\n            order.pay(redPacketPayAmount, capitalPayAmount);\n            try {\n                orderRepository.updateOrder(order);\n            } catch (OptimisticLockingFailureException e) {\n                //ignore the concurrently update order exception, ensure idempotency.\n            }\n        }\n\n        String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));\n        String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));\n    }\n\n    public void confirmMakePayment(String orderNo, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"order confirm make payment called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        Order foundOrder = orderRepository.findByMerchantOrderNo(orderNo);\n\n        //check if the trade order status is PAYING, if no, means another call confirmMakePayment happened, return directly, ensure idempotency.\n        if (foundOrder != null && foundOrder.getStatus().equals(\"PAYING\")) {\n            foundOrder.confirm();\n            orderRepository.updateOrder(foundOrder);\n        }\n    }\n\n    public void cancelMakePayment(String orderNo,  BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"order cancel make payment called.time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        Order foundOrder = orderRepository.findByMerchantOrderNo(orderNo);\n\n        //check if the trade order status is PAYING, if no, means another call cancelMakePayment happened, return directly, ensure idempotency.\n        if (foundOrder != null && foundOrder.getStatus().equals(\"PAYING\")) {\n            foundOrder.cancelPayment();\n            orderRepository.updateOrder(foundOrder);\n        }\n    }\n\n\n    private CapitalTradeOrderDto buildCapitalTradeOrderDto(Order order) {\n\n        CapitalTradeOrderDto tradeOrderDto = new CapitalTradeOrderDto();\n        tradeOrderDto.setAmount(order.getCapitalPayAmount());\n        tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());\n        tradeOrderDto.setSelfUserId(order.getPayerUserId());\n        tradeOrderDto.setOppositeUserId(order.getPayeeUserId());\n        tradeOrderDto.setOrderTitle(String.format(\"order no:%s\", order.getMerchantOrderNo()));\n\n        return tradeOrderDto;\n    }\n\n    private RedPacketTradeOrderDto buildRedPacketTradeOrderDto(Order order) {\n        RedPacketTradeOrderDto tradeOrderDto = new RedPacketTradeOrderDto();\n        tradeOrderDto.setAmount(order.getRedPacketPayAmount());\n        tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());\n        tradeOrderDto.setSelfUserId(order.getPayerUserId());\n        tradeOrderDto.setOppositeUserId(order.getPayeeUserId());\n        tradeOrderDto.setOrderTitle(String.format(\"order no:%s\", order.getMerchantOrderNo()));\n\n        return tradeOrderDto;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/service/PlaceOrderServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.order.service;\n\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.entity.Shop;\nimport io.tyloo.sample.order.domain.repository.ShopRepository;\nimport io.tyloo.sample.order.domain.service.OrderServiceImpl;\nimport org.apache.commons.lang3.tuple.Pair;\nimport io.tyloo.CancellingException;\nimport io.tyloo.ConfirmingException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n\n@Service\npublic class PlaceOrderServiceImpl {\n\n    @Autowired\n    ShopRepository shopRepository;\n\n    @Autowired\n    OrderServiceImpl orderService;\n\n    @Autowired\n    PaymentServiceImpl paymentService;\n\n    public String placeOrder(long payerUserId, long shopId, List<Pair<Long, Integer>> productQuantities, final BigDecimal redPacketPayAmount) {\n        Shop shop = shopRepository.findById(shopId);\n\n        final Order order = orderService.createOrder(payerUserId, shop.getOwnerUserId(), productQuantities);\n\n        Boolean result = false;\n\n        try {\n\n//            ExecutorService executorService = Executors.newFixedThreadPool(2);\n\n//            Future future1 = executorService.submit(new Runnable() {\n//                @Override\n//                public void run() {\n                    paymentService.makePayment(order.getMerchantOrderNo(), redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));\n//                }\n//            });\n\n//            Future future2 = executorService.submit(new Runnable() {\n//                @Override\n//                public void run() {\n//                    paymentService.makePayment(order.getMerchantOrderNo(), order, redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));\n//                }\n//            });\n//\n//            future1.get();\n//            future2.get();\n\n        } catch (ConfirmingException confirmingException) {\n            //exception throws with the tcc transaction status is CONFIRMING,\n            //when tcc transaction is confirming status,\n            // the tcc transaction recovery will try to confirm the whole transaction to ensure eventually consistent.\n\n            result = true;\n        } catch (CancellingException cancellingException) {\n            //exception throws with the tcc transaction status is CANCELLING,\n            //when tcc transaction is under CANCELLING status,\n            // the tcc transaction recovery will try to cancel the whole transaction to ensure eventually consistent.\n        } catch (Throwable e) {\n            //other exceptions throws at TRYING stage.\n            //you can retry or cancel the operation.\n            e.printStackTrace();\n        }\n\n        return order.getMerchantOrderNo();\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/web/controller/OrderController.java",
    "content": "package io.tyloo.sample.dubbo.order.web.controller;\n\nimport io.tyloo.sample.dubbo.order.service.AccountServiceImpl;\nimport io.tyloo.sample.dubbo.order.service.PlaceOrderServiceImpl;\nimport io.tyloo.sample.dubbo.order.web.controller.vo.PlaceOrderRequest;\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.entity.Product;\nimport io.tyloo.sample.order.domain.repository.ProductRepository;\nimport io.tyloo.sample.order.domain.service.OrderServiceImpl;\nimport org.apache.commons.lang3.tuple.ImmutablePair;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.view.RedirectView;\n\nimport java.math.BigDecimal;\nimport java.security.InvalidParameterException;\nimport java.util.List;\n\n\n@Controller\n@RequestMapping(\"\")\npublic class OrderController {\n\n    @Autowired\n    PlaceOrderServiceImpl placeOrderService;\n\n    @Autowired\n    ProductRepository productRepository;\n\n    @Autowired\n    AccountServiceImpl accountService;\n\n    @Autowired\n    OrderServiceImpl orderService;\n\n    @RequestMapping(value = \"/\", method = RequestMethod.GET)\n    public ModelAndView index() {\n        ModelAndView mv = new ModelAndView(\"/index\");\n        return mv;\n    }\n\n    @RequestMapping(value = \"/user/{userId}/shop/{shopId}\", method = RequestMethod.GET)\n    public ModelAndView getProductsInShop(@PathVariable long userId,\n                                          @PathVariable long shopId) {\n        List<Product> products = productRepository.findByShopId(shopId);\n\n        ModelAndView mv = new ModelAndView(\"/shop\");\n\n        mv.addObject(\"products\", products);\n        mv.addObject(\"userId\", userId);\n        mv.addObject(\"shopId\", shopId);\n\n        return mv;\n    }\n\n    @RequestMapping(value = \"/user/{userId}/shop/{shopId}/product/{productId}/confirm\", method = RequestMethod.GET)\n    public ModelAndView productDetail(@PathVariable long userId,\n                                      @PathVariable long shopId,\n                                      @PathVariable long productId) {\n\n        ModelAndView mv = new ModelAndView(\"product_detail\");\n\n        mv.addObject(\"capitalAmount\", accountService.getCapitalAccountByUserId(userId));\n        mv.addObject(\"redPacketAmount\", accountService.getRedPacketAccountByUserId(userId));\n\n        mv.addObject(\"product\", productRepository.findById(productId));\n\n        mv.addObject(\"userId\", userId);\n        mv.addObject(\"shopId\", shopId);\n\n        return mv;\n    }\n\n    @RequestMapping(value = \"/placeorder\", method = RequestMethod.POST)\n    public RedirectView placeOrder(@RequestParam String redPacketPayAmount,\n                                   @RequestParam long shopId,\n                                   @RequestParam long payerUserId,\n                                   @RequestParam long productId) {\n\n\n        PlaceOrderRequest request = buildRequest(redPacketPayAmount, shopId, payerUserId, productId);\n\n        String merchantOrderNo = placeOrderService.placeOrder(request.getPayerUserId(), request.getShopId(),\n                request.getProductQuantities(), request.getRedPacketPayAmount());\n\n        return new RedirectView(\"/payresult/\" + merchantOrderNo);\n    }\n\n    @RequestMapping(value = \"/payresult/{merchantOrderNo}\", method = RequestMethod.GET)\n    public ModelAndView getPayResult(@PathVariable String merchantOrderNo) {\n\n        ModelAndView mv = new ModelAndView(\"pay_success\");\n\n        String payResultTip = null;\n        Order foundOrder = orderService.findOrderByMerchantOrderNo(merchantOrderNo);\n\n        payResultTip = foundOrder.getStatus();\n\n        mv.addObject(\"payResult\", payResultTip);\n\n        mv.addObject(\"capitalAmount\", accountService.getCapitalAccountByUserId(foundOrder.getPayerUserId()));\n        mv.addObject(\"redPacketAmount\", accountService.getRedPacketAccountByUserId(foundOrder.getPayerUserId()));\n\n        return mv;\n    }\n\n\n    private PlaceOrderRequest buildRequest(String redPacketPayAmount, long shopId, long payerUserId, long productId) {\n        BigDecimal redPacketPayAmountInBigDecimal = new BigDecimal(redPacketPayAmount);\n        if (redPacketPayAmountInBigDecimal.compareTo(BigDecimal.ZERO) < 0) {\n            throw new InvalidParameterException(\"invalid red packet amount :\" + redPacketPayAmount);\n        }\n\n        PlaceOrderRequest request = new PlaceOrderRequest();\n        request.setPayerUserId(payerUserId);\n        request.setShopId(shopId);\n        request.setRedPacketPayAmount(new BigDecimal(redPacketPayAmount));\n        request.getProductQuantities().add(new ImmutablePair<Long, Integer>(productId, 1));\n        return request;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/web/controller/vo/PlaceOrderRequest.java",
    "content": "package io.tyloo.sample.dubbo.order.web.controller.vo;\n\nimport org.apache.commons.lang3.tuple.Pair;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic class PlaceOrderRequest {\n\n    private long payerUserId;\n\n    private long shopId;\n\n    private BigDecimal redPacketPayAmount;\n\n    private List<Pair<Long, Integer>> productQuantities = new ArrayList<Pair<Long, Integer>>();\n\n    public long getPayerUserId() {\n        return payerUserId;\n    }\n\n    public void setPayerUserId(long payerUserId) {\n        this.payerUserId = payerUserId;\n    }\n\n    public long getShopId() {\n        return shopId;\n    }\n\n    public void setShopId(long shopId) {\n        this.shopId = shopId;\n    }\n\n    public BigDecimal getRedPacketPayAmount() {\n        return redPacketPayAmount;\n    }\n\n    public void setRedPacketPayAmount(BigDecimal redPacketPayAmount) {\n        this.redPacketPayAmount = redPacketPayAmount;\n    }\n\n    public List<Pair<Long, Integer>> getProductQuantities() {\n        return productQuantities;\n    }\n\n    public void setProductQuantities(List<Pair<Long, Integer>> productQuantities) {\n        this.productQuantities = productQuantities;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/config/spring/local/appcontext-service-dubbo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <context:property-placeholder location=\"classpath:sample-dubbo-order.properties\"/>\n\n\n    <dubbo:application name=\"sample-dubbo-order\"/>\n\n    <dubbo:registry protocol=\"zookeeper\" address=\"${zookeeper.address}\"\n                    session=\"${zookeeper.session.timeout}\"\n                    timeout=\"${zookeeper.connect.timeout}\"/>\n\n    <dubbo:reference id=\"captialTradeOrderService\"\n                     interface=\"io.tyloo.sample.dubbo.capital.api.CapitalTradeOrderService\"\n                     timeout=\"60000\"/>\n\n    <dubbo:reference id=\"redPacketTradeOrderService\"\n                     interface=\"io.tyloo.sample.dubbo.redpacket.api.RedPacketTradeOrderService\"\n                     timeout=\"60000\"/>\n\n    <dubbo:reference id=\"redPacketAccountService\"\n                     interface=\"io.tyloo.sample.dubbo.redpacket.api.RedPacketAccountService\"\n                     timeout=\"60000\"/>\n\n    <dubbo:reference id=\"capitalAccountService\"\n                     interface=\"io.tyloo.sample.dubbo.capital.api.CapitalAccountService\"\n                     timeout=\"60000\"/>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/config/spring/local/appcontext-service-tcc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"30\"/>\n        <property name=\"recoverDuration\" value=\"30\"/>\n        <property name=\"cronExpression\" value=\"0/30 * * * * ?\"/>\n        <property name=\"delayCancelExceptions\">\n            <util:set>\n                <value>org.apache.dubbo.remoting.TimeoutException</value>\n            </util:set>\n        </property>\n    </bean>\n\n    <bean id=\"transactionRepository\"\n          class=\"io.tyloo.spring.repository.SpringJdbcTransactionRepository\">\n        <property name=\"dataSource\" ref=\"tylooTccDataSource\"/>\n        <property name=\"domain\" value=\"ORDER\"/>\n        <property name=\"tbSuffix\" value=\"_ORD\"/>\n    </bean>\n\n    <util:properties id=\"tccjdbc\" location=\"classpath:tccjdbc.properties\"/>\n\n    <bean id=\"tylooTccDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{tccjdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{tccjdbc['tcc.jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{tccjdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{tccjdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{tccjdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{tccjdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{tccjdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{tccjdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{tccjdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{tccjdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <!--<bean id=\"transactionRepository\" class=\"FileSystemTransactionRepository\">-->\n    <!--<property name=\"rootPath\" value=\"/data/tcc\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"transactionRepository\"-->\n    <!--class=\"io.tyloo.repository.ZooKeeperTransactionRepository\">-->\n    <!--&lt;!&ndash;<property name=\"zkServers\" value=\"localhost:2181,localhost:2183,localhost:2185\"/>&ndash;&gt;-->\n    <!--<property name=\"zkServers\" value=\"localhost:2181\"/>-->\n    <!--<property name=\"zkTimeout\" value=\"10000\"/>-->\n    <!--<property name=\"zkRootPath\" value=\"/tcc_ut\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"transactionRepository\" class=\"RedisTransactionRepository\">-->\n    <!--<property name=\"keyPrefix\" value=\"TCC:ORD:\"/>-->\n    <!--<property name=\"jedisPool\" ref=\"jedisPool\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">-->\n    <!--<property name=\"maxTotal\" value=\"1000\"/>-->\n    <!--<property name=\"maxWaitMillis\" value=\"1000\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">-->\n    <!--<constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>-->\n    <!--<constructor-arg index=\"1\" value=\"127.0.0.1\"/>-->\n    <!--<constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>-->\n    <!--<constructor-arg index=\"3\" value=\"1000\" type=\"int\"/>-->\n    <!--<constructor-arg index=\"4\" type=\"java.lang.String\">-->\n    <!--<null/>-->\n    <!--</constructor-arg>-->\n    <!--<constructor-arg index=\"5\" value=\"0\" type=\"int\"/>-->\n    <!--</bean>-->\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/config/spring/local/appcontext-web-servlet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd\">\n\n       <mvc:annotation-driven/>\n       <context:component-scan base-package=\"io.tyloo.sample.dubbo\"/>\n       <aop:aspectj-autoproxy proxy-target-class=\"true\"/>\n\n\n       <bean id=\"contentNegotiationManager\"\n             class=\"org.springframework.web.accept.ContentNegotiationManagerFactoryBean\">\n              <property name=\"favorParameter\" value=\"false\"/>\n              <property name=\"ignoreAcceptHeader\" value=\"true\"/>\n              <property name=\"favorPathExtension\" value=\"false\"/>\n              <property name=\"mediaTypes\">\n                     <value>\n                            json=application/json\n                            html=text/html\n                     </value>\n              </property>\n              <property name=\"defaultContentType\" value=\"text/html\"/>\n       </bean>\n\n       <bean class=\"org.springframework.web.servlet.view.ContentNegotiatingViewResolver\">\n              <property name=\"contentNegotiationManager\" ref=\"contentNegotiationManager\"/>\n              <property name=\"order\" value=\"1\"/>\n              <property name=\"viewResolvers\">\n                     <list>\n                            <!-- ===================================================== -->\n                            <!-- ViewResolver For FreeMarker -->\n                            <!-- ===================================================== -->\n                            <bean id=\"freemarkerResolver\"\n                                  class=\"org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver\">\n\n                                   <property name=\"suffix\" value=\".ftl\"/>\n                                   <property name=\"contentType\" value=\"text/html;charset=utf-8\"/>\n                                   <property name=\"viewClass\">\n                                          <value>org.springframework.web.servlet.view.freemarker.FreeMarkerView</value>\n                                   </property>\n                                   <!--<property name=\"attributesMap\">\n                                       <map>\n                                           <entry key=\"truncate\"><bean class=\"com.rj8g.linkage.renba.web.TruncateTemplateMethodModel\" /></entry>\n                                       </map>\n                                   </property>-->\n                            </bean>\n                     </list>\n              </property>\n\n              <!--<property name=\"defaultViews\">-->\n                     <!--<list>-->\n                            <!--<bean class=\"org.springframework.web.servlet.view.json.MappingJackson2JsonView\"/>-->\n                     <!--</list>-->\n              <!--</property>-->\n       </bean>\n\n       <bean id=\"freemarkerConfig\" class=\"org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer\">\n              <property name=\"templateLoaderPath\">\n                     <value>/WEB-INF/ftl/</value>\n              </property>\n              <property name=\"freemarkerSettings\"><!-- 设置FreeMarker环境属性 -->\n                     <props>\n                            <prop key=\"template_update_delay\">5</prop>\n                            <!--刷新模板的周期，单位为秒 -->\n                            <prop key=\"default_encoding\">UTF-8</prop>\n                            <!--模板的编码格式 -->\n                            <prop key=\"locale\">UTF-8</prop>\n                            <!-- 本地化设置 -->\n                            <prop key=\"datetime_format\">yyyy-MM-dd HH:mm:ss</prop>\n                            <prop key=\"time_format\">HH:mm:ss</prop>\n                            <prop key=\"number_format\">0.####</prop>\n                            <prop key=\"boolean_format\">true,false</prop>\n                            <prop key=\"whitespace_stripping\">true</prop>\n                            <prop key=\"tag_syntax\">auto_detect</prop>\n                            <prop key=\"url_escaping_charset\">UTF-8</prop>\n                            <prop key=\"classic_compatible\">true</prop>\n                     </props>\n              </property>\n\n              <property name=\"freemarkerVariables\">\n                     <map>\n                            <entry key=\"xml_escape\" value-ref=\"fmXmlEscape\"/>\n                            <entry key=\"html_escape\" value-ref=\"fmHtmlEscape\"/>\n                     </map>\n              </property>\n\n       </bean>\n\n       <bean id=\"fmXmlEscape\" class=\"freemarker.template.utility.XmlEscape\"/>\n       <bean id=\"fmHtmlEscape\" class=\"freemarker.template.utility.HtmlEscape\"/>\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-dubbo-order]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/sample-dubbo-order.properties",
    "content": "\n\nzookeeper.address=127.0.0.1:2181\nzookeeper.session.timeout=8000\nzookeeper.connect.timeout=2000"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/tccjdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=2000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/index.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>tcc transacton dubbo sample order</title>\n</head>\n<body>\n<div class=\"page\">\n    <b>sample说明：</b>\n    <br/>\n    打开下面商品列表链接，选择一个商品购买，输入红包支付金额，进行支付，系统将使用红包＋资金账户转账支付。\n    <br/>\n    支付成功后，各个project会打印如下日志：\n    <br/>\n    <br>\n    &nbsp;&nbsp; <b>sample-dubbo-order:</b>\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; order try make payment called\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; order confirm make payment called\n    <br/>\n    <br/>\n    &nbsp;&nbsp; <b>sample-dubbo-capital:</b>\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; capital try record called\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; capital confirm record called\n    <br/>\n    <br/>\n    &nbsp;&nbsp; <b>sample-dubbo-redpacket:</b>\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; red packet try record called\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; red packet confirm record called\n    <p/>\n    <a href=\"/user/2000/shop/1\">\n        商品列表链接\n    </a>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/pay_success.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>支付结果</title>\n</head>\n<body>\n<div class=\"page\">\n    <p>支付状态:<b>${payResult} </b> </p>\n\n    <p>剩余可用账户余额: ${capitalAmount?string(\"0.00\")}元</p>\n    <p>剩余可用红包余额: ${redPacketAmount?string(\"0.00\")}元</p>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/product_detail.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>订单详情</title>\n</head>\n<body>\n<div class=\"page\">\n    <p>名称: ${product.productName}</p>\n    <p>价格: ${product.price?string(\"0.00\")}元</p>\n\n    <p>可用账户余额: ${capitalAmount?string(\"0.00\")}元</p>\n    <p>可用红包余额: ${redPacketAmount?string(\"0.00\")}元</p>\n\n    <form action=\"/placeorder\" method=\"post\">\n        红包金额:&nbsp;&nbsp;&nbsp;<input type=\"text\" style=\"width: 220px\" name=\"redPacketPayAmount\" value=\"\" placeholder=\"请输入期望使用的红包金额\"/>\n        <input type=\"hidden\" name=\"shopId\" value=\"${shopId}\" />\n        <input type=\"hidden\" name=\"productId\" value=\"${product.productId}\"/>\n        <input type=\"hidden\" name=\"payerUserId\" value=\"${userId}\"/>\n\n        <input type=\"submit\" value=\"支付\"/>\n    </form>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/shop.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>商品列表</title>\n</head>\n<body>\n<div class=\"page\">\n    <div class=\"bg-f\">\n        <ul class=\"list\" >\n        [#if products?size > 0]\n            [#list products as product]\n                <li class=\"list-item\">\n                    <p>${product.productName}(${product.price?string(\"0.00\")})&nbsp;&nbsp;&nbsp;&nbsp;<span><a href=\"/user/${userId}/shop/${shopId}/product/${product.productId}/confirm\">购买</a></span></p>\n                </li>\n            [/#list]\n        [/#if]\n        </ul>\n    </div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/web.xml",
    "content": "<!DOCTYPE web-app PUBLIC\n        \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\n        \"http://java.sun.com/dtd/web-app_2_3.dtd\" >\n\n<web-app>\n    <display-name>Sample Dubbo Order</display-name>\n\n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml,classpath:tyloo-dubbo.xml\n        </param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jConfigLocation</param-name>\n        <param-value>classpath:log/log4j.xml</param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jRefreshInterval</param-name>\n        <param-value>60000</param-value>\n    </context-param>\n\n    <listener>\n        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n    </listener>\n\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n\n    <filter>\n        <filter-name>characterEncodingFilter</filter-name>\n        <display-name>Character Encoding Filter</display-name>\n        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>\n        <init-param>\n            <param-name>encoding</param-name>\n            <param-value>UTF-8</param-value>\n        </init-param>\n        <init-param>\n            <param-name>forceEncoding</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>characterEncodingFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n    </filter-mapping>\n\n    <servlet>\n        <servlet-name>springmvc</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>classpath:config/spring/local/appcontext-web-servlet.xml</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>springmvc</servlet-name>\n        <url-pattern>/*</url-pattern>\n    </servlet-mapping>\n\n</web-app>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-dubbo-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo-redpacket</artifactId>\n    <packaging>war</packaging>\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo-redpacket-api</artifactId>\n           <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-dubbo</artifactId>\n           <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-redpacket</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webmvc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/java/io/tyloo/sample/dubbo/redpacket/service/RedPacketAccountServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.redpacket.service;\n\nimport io.tyloo.sample.dubbo.redpacket.api.RedPacketAccountService;\nimport io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\n\n\n@Service(\"redPacketAccountService\")\npublic class RedPacketAccountServiceImpl implements RedPacketAccountService {\n\n    @Autowired\n    RedPacketAccountRepository redPacketAccountRepository;\n\n    @Override\n    public BigDecimal getRedPacketAccountByUserId(long userId) {\n        return redPacketAccountRepository.findByUserId(userId).getBalanceAmount();\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/java/io/tyloo/sample/dubbo/redpacket/service/RedPacketTradeOrderServiceImpl.java",
    "content": "package io.tyloo.sample.dubbo.redpacket.service;\n\nimport io.tyloo.sample.dubbo.redpacket.api.RedPacketTradeOrderService;\nimport io.tyloo.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;\nimport io.tyloo.sample.redpacket.domain.entity.TradeOrder;\nimport io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;\nimport io.tyloo.sample.redpacket.domain.repository.TradeOrderRepository;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.dubbo.context.DubboTransactionContextEditor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataIntegrityViolationException;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Calendar;\n\n\n@Service(\"redPacketTradeOrderService\")\npublic class RedPacketTradeOrderServiceImpl implements RedPacketTradeOrderService {\n\n    @Autowired\n    RedPacketAccountRepository redPacketAccountRepository;\n\n    @Autowired\n    TradeOrderRepository tradeOrderRepository;\n\n    @Override\n    @Tyloo(confirmMethod = \"confirmRecord\", cancelMethod = \"cancelRecord\", transactionContextEditor = DubboTransactionContextEditor.class)\n    @Transactional\n    public String record(RedPacketTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"red packet try record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if trade order has been recorded, if yes, return success directly.\n        if (foundTradeOrder == null) {\n\n            TradeOrder tradeOrder = new TradeOrder(\n                    tradeOrderDto.getSelfUserId(),\n                    tradeOrderDto.getOppositeUserId(),\n                    tradeOrderDto.getMerchantOrderNo(),\n                    tradeOrderDto.getAmount()\n            );\n\n            try {\n\n                tradeOrderRepository.insert(tradeOrder);\n\n                RedPacketAccount transferFromAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n                transferFromAccount.transferFrom(tradeOrderDto.getAmount());\n\n                redPacketAccountRepository.save(transferFromAccount);\n            } catch (DataIntegrityViolationException e) {\n                //this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.\n            }\n        }\n\n        return \"success\";\n    }\n\n    @Transactional\n    public void confirmRecord(RedPacketTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"red packet confirm record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.\n        if (tradeOrder != null && tradeOrder.getStatus().equals(\"DRAFT\")) {\n            tradeOrder.confirm();\n            tradeOrderRepository.update(tradeOrder);\n\n            RedPacketAccount transferToAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());\n\n            transferToAccount.transferTo(tradeOrderDto.getAmount());\n\n            redPacketAccountRepository.save(transferToAccount);\n        }\n    }\n\n    @Transactional\n    public void cancelRecord(RedPacketTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"red packet cancel record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.\n        if (null != tradeOrder && \"DRAFT\".equals(tradeOrder.getStatus())) {\n            tradeOrder.cancel();\n            tradeOrderRepository.update(tradeOrder);\n\n            RedPacketAccount capitalAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n            capitalAccount.cancelTransfer(tradeOrderDto.getAmount());\n\n            redPacketAccountRepository.save(capitalAccount);\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/config/spring/local/appcontext-service-provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n\n    <context:component-scan base-package=\"io.tyloo.sample.dubbo\"/>\n\n    <dubbo:application name=\"sample-dubbo-redpacket\"/>\n\n    <context:property-placeholder location=\"classpath:sample-dubbo-redpacket.properties\"\n                                  ignore-unresolvable=\"true\"/>\n\n    <dubbo:registry id=\"redPacketRpcZk\" protocol=\"zookeeper\" address=\"${zookeeper.address}\"\n                    session=\"${zookeeper.session.timeout}\"\n                    timeout=\"${zookeeper.connect.timeout}\"/>\n\n\n    <dubbo:protocol name=\"dubbo\" port=\"${dubbo.port}\"/>\n\n    <dubbo:service interface=\"io.tyloo.sample.dubbo.redpacket.api.RedPacketTradeOrderService\"\n                   registry=\"redPacketRpcZk\"\n                   ref=\"redPacketTradeOrderService\" timeout=\"60000\" retries=\"0\"/>\n\n    <dubbo:service interface=\"io.tyloo.sample.dubbo.redpacket.api.RedPacketAccountService\"\n                   registry=\"redPacketRpcZk\"\n                   ref=\"redPacketAccountService\" timeout=\"60000\" retries=\"0\"/>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/config/spring/local/appcontext-service-tcc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample.dubbo\"/>\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"30\"/>\n        <property name=\"recoverDuration\" value=\"60\"/>\n        <property name=\"cronExpression\" value=\"0/30 * * * * ?\"/>\n    </bean>\n\n\n    <bean id=\"transactionRepository\" class=\"io.tyloo.spring.repositorySpringJdbcTransactionRepository\">\n        <property name=\"dataSource\" ref=\"tylooTccDataSource\"/>\n        <property name=\"domain\" value=\"REDPACKET\"/>\n        <property name=\"tbSuffix\" value=\"_RED\"/>\n    </bean>\n\n    <util:properties id=\"tccjdbc\" location=\"classpath:tccjdbc.properties\"/>\n\n    <bean id=\"tylooTccDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{tccjdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{tccjdbc['tcc.jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{tccjdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{tccjdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{tccjdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{tccjdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{tccjdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{tccjdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{tccjdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{tccjdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n\n    <!--<bean id=\"transactionRepository\" class=\"RedisTransactionRepository\">-->\n    <!--<property name=\"keyPrefix\" value=\"TCC:RED:\"/>-->\n    <!--<property name=\"jedisPool\" ref=\"jedisPool\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">-->\n    <!--<property name=\"maxTotal\" value=\"1000\"/>-->\n    <!--<property name=\"maxWaitMillis\" value=\"1000\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">-->\n    <!--<constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>-->\n    <!--<constructor-arg index=\"1\" value=\"127.0.0.1\"/>-->\n    <!--<constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>-->\n    <!--<constructor-arg index=\"3\" value=\"1000\" type=\"int\"/>-->\n    <!--<constructor-arg index=\"4\" type=\"java.lang.String\">-->\n    <!--<null/>-->\n    <!--</constructor-arg>-->\n    <!--<constructor-arg index=\"5\" value=\"0\" type=\"int\"/>-->\n    <!--</bean>-->\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/config/spring/local/appcontext-web-servlet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">\n       \n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-dubbo-redpacket]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/sample-dubbo-redpacket.properties",
    "content": "dubbo.port=2880\n\nzookeeper.address=127.0.0.1:2181\nzookeeper.session.timeout=30000\nzookeeper.connect.timeout=2000"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/tccjdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=30000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/webapp/WEB-INF/web.xml",
    "content": "<!DOCTYPE web-app PUBLIC\n        \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\n        \"http://java.sun.com/dtd/web-app_2_3.dtd\" >\n\n<web-app>\n    <display-name>Sample Dubbo Redpacket</display-name>\n\n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml,classpath:tyloo-dubbo.xml\n        </param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jConfigLocation</param-name>\n        <param-value>classpath:log/log4j.xml</param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jRefreshInterval</param-name>\n        <param-value>60000</param-value>\n    </context-param>\n\n    <listener>\n        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n    </listener>\n\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n\n    <filter>\n        <filter-name>characterEncodingFilter</filter-name>\n        <display-name>Character Encoding Filter</display-name>\n        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>\n        <init-param>\n            <param-name>encoding</param-name>\n            <param-value>UTF-8</param-value>\n        </init-param>\n        <init-param>\n            <param-name>forceEncoding</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>characterEncodingFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n    </filter-mapping>\n\n    <servlet>\n        <servlet-name>springmvc</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>classpath:config/spring/local/appcontext-web-servlet.xml</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>springmvc</servlet-name>\n        <url-pattern>/</url-pattern>\n    </servlet-mapping>\n\n</web-app>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/webapp/index.jsp",
    "content": "hello tcc transacton dubbo sample red packet"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-dubbo-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-dubbo-redpacket-api</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/src/main/java/io/tyloo/sample/dubbo/redpacket/api/RedPacketAccountService.java",
    "content": "package io.tyloo.sample.dubbo.redpacket.api;\n\nimport java.math.BigDecimal;\n\n\npublic interface RedPacketAccountService {\n    BigDecimal getRedPacketAccountByUserId(long userId);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/src/main/java/io/tyloo/sample/dubbo/redpacket/api/RedPacketTradeOrderService.java",
    "content": "package io.tyloo.sample.dubbo.redpacket.api;\n\nimport io.tyloo.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.api.Tyloo;\n\n\npublic interface RedPacketTradeOrderService {\n\n    @Tyloo\n    public String record(RedPacketTradeOrderDto tradeOrderDto);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/src/main/java/io/tyloo/sample/dubbo/redpacket/api/dto/RedPacketTradeOrderDto.java",
    "content": "package io.tyloo.sample.dubbo.redpacket.api.dto;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\npublic class RedPacketTradeOrderDto implements Serializable {\n\n    private static final long serialVersionUID = 4747014387277477558L;\n\n    private long selfUserId;\n\n    private long oppositeUserId;\n\n    private String orderTitle;\n\n    private String merchantOrderNo;\n\n    private BigDecimal amount;\n\n    public long getSelfUserId() {\n        return selfUserId;\n    }\n\n    public void setSelfUserId(long selfUserId) {\n        this.selfUserId = selfUserId;\n    }\n\n    public long getOppositeUserId() {\n        return oppositeUserId;\n    }\n\n    public void setOppositeUserId(long oppositeUserId) {\n        this.oppositeUserId = oppositeUserId;\n    }\n\n    public String getOrderTitle() {\n        return orderTitle;\n    }\n\n    public void setOrderTitle(String orderTitle) {\n        this.orderTitle = orderTitle;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public void setMerchantOrderNo(String merchantOrderNo) {\n        this.merchantOrderNo = merchantOrderNo;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-tutorial-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-http-sample</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>tyloo-http-capital</module>\n        <module>tyloo-http-capital-api</module>\n        <module>tyloo-http-order</module>\n        <module>tyloo-http-redpacket</module>\n        <module>tyloo-http-redpacket-api</module>\n    </modules>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-http-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-http-capital</artifactId>\n    <packaging>war</packaging>\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-http-capital-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-captial</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webmvc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/java/io/tyloo/sample/http/capital/service/CapitalAccountServiceImpl.java",
    "content": "package io.tyloo.sample.http.capital.service;\n\nimport io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;\nimport io.tyloo.sample.http.capital.api.CapitalAccountService;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.math.BigDecimal;\n\n\npublic class CapitalAccountServiceImpl implements CapitalAccountService {\n\n\n    @Autowired\n    CapitalAccountRepository capitalAccountRepository;\n\n    @Override\n    public BigDecimal getCapitalAccountByUserId(long userId) {\n        return capitalAccountRepository.findByUserId(userId).getBalanceAmount();\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/java/io/tyloo/sample/http/capital/service/CapitalTradeOrderServiceImpl.java",
    "content": "package io.tyloo.sample.http.capital.service;\n\nimport io.tyloo.sample.capital.domain.entity.CapitalAccount;\nimport io.tyloo.sample.capital.domain.entity.TradeOrder;\nimport io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;\nimport io.tyloo.sample.capital.domain.repository.TradeOrderRepository;\nimport io.tyloo.sample.http.capital.api.CapitalTradeOrderService;\nimport io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.context.MethodTransactionContextEditor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataIntegrityViolationException;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Calendar;\n\n\npublic class CapitalTradeOrderServiceImpl implements CapitalTradeOrderService {\n\n    @Autowired\n    CapitalAccountRepository capitalAccountRepository;\n\n    @Autowired\n    TradeOrderRepository tradeOrderRepository;\n\n    @Override\n    @Tyloo(confirmMethod = \"confirmRecord\", cancelMethod = \"cancelRecord\", transactionContextEditor = MethodTransactionContextEditor.class)\n    @Transactional\n    public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"capital try record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if trade order has been recorded, if yes, return success directly.\n        if (foundTradeOrder == null) {\n\n            TradeOrder tradeOrder = new TradeOrder(\n                    tradeOrderDto.getSelfUserId(),\n                    tradeOrderDto.getOppositeUserId(),\n                    tradeOrderDto.getMerchantOrderNo(),\n                    tradeOrderDto.getAmount()\n            );\n\n            try {\n                tradeOrderRepository.insert(tradeOrder);\n\n                CapitalAccount transferFromAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n                transferFromAccount.transferFrom(tradeOrderDto.getAmount());\n\n                capitalAccountRepository.save(transferFromAccount);\n            } catch (DataIntegrityViolationException e) {\n                //this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.\n            }\n        }\n\n        return \"success\";\n    }\n\n    @Transactional\n    public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"capital confirm record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.\n        if (null != tradeOrder && \"DRAFT\".equals(tradeOrder.getStatus())) {\n            tradeOrder.confirm();\n            tradeOrderRepository.update(tradeOrder);\n\n            CapitalAccount transferToAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());\n\n            transferToAccount.transferTo(tradeOrderDto.getAmount());\n\n            capitalAccountRepository.save(transferToAccount);\n        }\n    }\n\n    @Transactional\n    public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"capital cancel record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.\n        if (null != tradeOrder && \"DRAFT\".equals(tradeOrder.getStatus())) {\n            tradeOrder.cancel();\n            tradeOrderRepository.update(tradeOrder);\n\n            CapitalAccount capitalAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n            capitalAccount.cancelTransfer(tradeOrderDto.getAmount());\n\n            capitalAccountRepository.save(capitalAccount);\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/config/spring/local/appcontext-service-provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <bean name=\"capitalTradeOrderService\"\n          class=\"io.tyloo.sample.http.capital.service.CapitalTradeOrderServiceImpl\"/>\n\n    <bean name=\"capitalAccountService\"\n          class=\"io.tyloo.sample.http.capital.service.CapitalAccountServiceImpl\"/>\n\n    <bean name=\"capitalTradeOrderServiceExporter\"\n          class=\"org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter\">\n        <property name=\"service\" ref=\"capitalTradeOrderService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.capital.api.CapitalTradeOrderService\"/>\n    </bean>\n\n    <bean name=\"capitalAccountServiceExporter\"\n          class=\"org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter\">\n        <property name=\"service\" ref=\"capitalAccountService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.capital.api.CapitalAccountService\"/>\n    </bean>\n\n\n    <bean id=\"httpServer\"\n          class=\"org.springframework.remoting.support.SimpleHttpServerFactoryBean\">\n        <property name=\"contexts\">\n            <util:map>\n                <entry key=\"/remoting/CapitalTradeOrderService\" value-ref=\"capitalTradeOrderServiceExporter\"/>\n                <entry key=\"/remoting/CapitalAccountService\" value-ref=\"capitalAccountServiceExporter\"/>\n            </util:map>\n        </property>\n        <property name=\"port\" value=\"8090\"/>\n    </bean>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/config/spring/local/appcontext-service-tcc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample\"/>\n\n    <util:properties id=\"tccjdbc\" location=\"classpath:tccjdbc.properties\"/>\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"5\"/>\n        <property name=\"recoverDuration\" value=\"60\"/>\n        <property name=\"cronExpression\" value=\"0/30 * * * * ?\"/>\n    </bean>\n\n    <bean id=\"transactionRepository\" class=\"io.tyloo.spring.repository.SpringJdbcTransactionRepository\">\n    <property name=\"dataSource\" ref=\"tylooTccDataSource\"/>\n    <property name=\"domain\" value=\"CAPITAL\"/>\n    <property name=\"tbSuffix\" value=\"_CAP\"/>\n    </bean>\n\n    <bean id=\"tylooTccDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{tccjdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{tccjdbc['tcc.jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{tccjdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{tccjdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{tccjdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{tccjdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{tccjdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{tccjdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{tccjdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{tccjdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <bean id=\"kryoSerializer\" class=\"io.tyloo.serializer.KryoPoolSerializer\"/>\n\n<!--     <bean id=\"transactionRepository\" class=\"RedisTransactionRepository\">\n        <property name=\"keyPrefix\" value=\"TCC:CAP:\"/>\n        <property name=\"jedisPool\" ref=\"jedisPool\"/>\n        <property name=\"serializer\" ref=\"kryoSerializer\"/>\n    </bean>\n\n    <bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">\n        <property name=\"maxTotal\" value=\"1000\"/>\n        <property name=\"maxWaitMillis\" value=\"1000\"/>\n    </bean>\n\n    <bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">\n        <constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>\n        <constructor-arg index=\"1\" value=\"127.0.0.1\"/>\n        <constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>\n        <constructor-arg index=\"3\" value=\"1000\" type=\"int\"/>\n        <constructor-arg index=\"4\" type=\"java.lang.String\">\n            <null/>\n        </constructor-arg>\n        <constructor-arg index=\"5\" value=\"0\" type=\"int\"/>\n    </bean> -->\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/config/spring/local/appcontext-web-servlet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n       <context:component-scan base-package=\"io.tyloo.sample\"/>\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-http-capital]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/tccjdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_CAP?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=30000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/webapp/WEB-INF/web.xml",
    "content": "<!DOCTYPE web-app PUBLIC\n        \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\n        \"http://java.sun.com/dtd/web-app_2_3.dtd\" >\n\n<web-app>\n    <display-name>Sample Http Capital</display-name>\n\t<context-param>\n\t    <param-name>webAppRootKey</param-name>\n\t    <param-value>tyloo-http-capital</param-value>\n\t</context-param>\n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml\n        </param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jConfigLocation</param-name>\n        <param-value>classpath:log/log4j.xml</param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jRefreshInterval</param-name>\n        <param-value>60000</param-value>\n    </context-param>\n\n    <listener>\n        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n    </listener>\n\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n\n    <filter>\n        <filter-name>characterEncodingFilter</filter-name>\n        <display-name>Character Encoding Filter</display-name>\n        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>\n        <init-param>\n            <param-name>encoding</param-name>\n            <param-value>UTF-8</param-value>\n        </init-param>\n        <init-param>\n            <param-name>forceEncoding</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>characterEncodingFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n    </filter-mapping>\n\n    <servlet>\n        <servlet-name>springmvc</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>classpath:config/spring/local/appcontext-web-servlet.xml</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>springmvc</servlet-name>\n        <url-pattern>/</url-pattern>\n    </servlet-mapping>\n\n\n</web-app>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/webapp/index.jsp",
    "content": "hello tcc transacton http sample capital"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-http-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-http-capital-api</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/src/main/java/io/tyloo/sample/http/capital/api/CapitalAccountService.java",
    "content": "package io.tyloo.sample.http.capital.api;\n\nimport java.math.BigDecimal;\n\npublic interface CapitalAccountService {\n\n    BigDecimal getCapitalAccountByUserId(long userId);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/src/main/java/io/tyloo/sample/http/capital/api/CapitalTradeOrderService.java",
    "content": "package io.tyloo.sample.http.capital.api;\n\nimport io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;\nimport io.tyloo.api.TransactionContext;\n\n\npublic interface CapitalTradeOrderService {\n    public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/src/main/java/io/tyloo/sample/http/capital/api/dto/CapitalTradeOrderDto.java",
    "content": "package io.tyloo.sample.http.capital.api.dto;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\npublic class CapitalTradeOrderDto implements Serializable {\n\n    private static final long serialVersionUID = 6627401903410124642L;\n    \n    private long selfUserId;\n\n    private long oppositeUserId;\n\n    private String orderTitle;\n\n    private String merchantOrderNo;\n\n    private BigDecimal amount;\n\n    public long getSelfUserId() {\n        return selfUserId;\n    }\n\n    public void setSelfUserId(long selfUserId) {\n        this.selfUserId = selfUserId;\n    }\n\n    public long getOppositeUserId() {\n        return oppositeUserId;\n    }\n\n    public void setOppositeUserId(long oppositeUserId) {\n        this.oppositeUserId = oppositeUserId;\n    }\n\n    public String getOrderTitle() {\n        return orderTitle;\n    }\n\n    public void setOrderTitle(String orderTitle) {\n        this.orderTitle = orderTitle;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public void setMerchantOrderNo(String merchantOrderNo) {\n        this.merchantOrderNo = merchantOrderNo;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-http-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-http-order</artifactId>\n    <packaging>war</packaging>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-http-capital-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-http-redpacket-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-order</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-codec</groupId>\n            <artifactId>commons-codec</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webmvc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.freemarker</groupId>\n            <artifactId>freemarker</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/AccountServiceImpl.java",
    "content": "package io.tyloo.sample.http.order.service;\n\nimport io.tyloo.sample.http.capital.api.CapitalAccountService;\nimport io.tyloo.sample.http.redpacket.api.RedPacketAccountService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\n\n\n@Service(\"accountService\")\npublic class AccountServiceImpl {\n\n    @Autowired\n    RedPacketAccountService redPacketAccountService;\n\n    @Autowired\n    CapitalAccountService capitalAccountService;\n\n\n    public BigDecimal getRedPacketAccountByUserId(long userId){\n        return redPacketAccountService.getRedPacketAccountByUserId(userId);\n    }\n\n    public BigDecimal getCapitalAccountByUserId(long userId){\n        return capitalAccountService.getCapitalAccountByUserId(userId);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/PaymentServiceImpl.java",
    "content": "package io.tyloo.sample.http.order.service;\n\nimport io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;\nimport io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.repository.OrderRepository;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport io.tyloo.api.Tyloo;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.OptimisticLockingFailureException;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.math.BigDecimal;\nimport java.util.Calendar;\n\n\n@Service\npublic class PaymentServiceImpl {\n\n    @Autowired\n    TradeOrderServiceProxy tradeOrderServiceProxy;\n\n    @Autowired\n    OrderRepository orderRepository;\n\n\n    @Tyloo(confirmMethod = \"confirmMakePayment\", cancelMethod = \"cancelMakePayment\", asyncConfirm = true)\n    @Transactional\n    public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n\n        System.out.println(\"order try make payment called.time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        //check if the order status is DRAFT, if no, means that another call makePayment for the same order happened, ignore this call makePayment.\n        if (order.getStatus().equals(\"DRAFT\")) {\n\n            order.pay(redPacketPayAmount, capitalPayAmount);\n            try {\n                orderRepository.updateOrder(order);\n            } catch (OptimisticLockingFailureException e) {\n                //ignore the concurrently update order exception, ensure idempotency.\n            }\n        }\n\n        String result = tradeOrderServiceProxy.record(null, buildCapitalTradeOrderDto(order));\n        String result2 = tradeOrderServiceProxy.record(null, buildRedPacketTradeOrderDto(order));\n    }\n\n    public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"order confirm make payment called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        Order foundOrder = orderRepository.findByMerchantOrderNo(order.getMerchantOrderNo());\n\n        //check order status, only if the status equals DRAFT, then confirm order\n        if (foundOrder != null && foundOrder.getStatus().equals(\"PAYING\")) {\n            order.confirm();\n            orderRepository.updateOrder(order);\n        }\n    }\n\n    public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"order cancel make payment called.time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        Order foundOrder = orderRepository.findByMerchantOrderNo(order.getMerchantOrderNo());\n\n        if (foundOrder != null && foundOrder.getStatus().equals(\"PAYING\")) {\n            order.cancelPayment();\n            orderRepository.updateOrder(order);\n        }\n    }\n\n\n    private CapitalTradeOrderDto buildCapitalTradeOrderDto(Order order) {\n\n        CapitalTradeOrderDto tradeOrderDto = new CapitalTradeOrderDto();\n        tradeOrderDto.setAmount(order.getCapitalPayAmount());\n        tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());\n        tradeOrderDto.setSelfUserId(order.getPayerUserId());\n        tradeOrderDto.setOppositeUserId(order.getPayeeUserId());\n        tradeOrderDto.setOrderTitle(String.format(\"order no:%s\", order.getMerchantOrderNo()));\n\n        return tradeOrderDto;\n    }\n\n    private RedPacketTradeOrderDto buildRedPacketTradeOrderDto(Order order) {\n        RedPacketTradeOrderDto tradeOrderDto = new RedPacketTradeOrderDto();\n        tradeOrderDto.setAmount(order.getRedPacketPayAmount());\n        tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());\n        tradeOrderDto.setSelfUserId(order.getPayerUserId());\n        tradeOrderDto.setOppositeUserId(order.getPayeeUserId());\n        tradeOrderDto.setOrderTitle(String.format(\"order no:%s\", order.getMerchantOrderNo()));\n\n        return tradeOrderDto;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/PlaceOrderServiceImpl.java",
    "content": "package io.tyloo.sample.http.order.service;\n\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.entity.Shop;\nimport io.tyloo.sample.order.domain.repository.ShopRepository;\nimport io.tyloo.sample.order.domain.service.OrderServiceImpl;\nimport org.apache.commons.lang3.tuple.Pair;\nimport io.tyloo.CancellingException;\nimport io.tyloo.ConfirmingException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n\n@Service\npublic class PlaceOrderServiceImpl {\n\n    @Autowired\n    ShopRepository shopRepository;\n\n    @Autowired\n    OrderServiceImpl orderService;\n\n    @Autowired\n    PaymentServiceImpl paymentService;\n\n\n    public String placeOrder(long payerUserId, long shopId, List<Pair<Long, Integer>> productQuantities, BigDecimal redPacketPayAmount) {\n        Shop shop = shopRepository.findById(shopId);\n\n        Order order = orderService.createOrder(payerUserId, shop.getOwnerUserId(), productQuantities);\n\n        Boolean result = false;\n\n        try {\n            paymentService.makePayment(order, redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));\n\n        } catch (ConfirmingException confirmingException) {\n            //exception throws with the tcc transaction status is CONFIRMING,\n            //when tcc transaction is confirming status,\n            // the tcc transaction recovery will try to confirm the whole transaction to ensure eventually consistent.\n\n            result = true;\n        } catch (CancellingException cancellingException) {\n            //exception throws with the tcc transaction status is CANCELLING,\n            //when tcc transaction is under CANCELLING status,\n            // the tcc transaction recovery will try to cancel the whole transaction to ensure eventually consistent.\n        } catch (Throwable e) {\n            //other exceptions throws at TRYING stage.\n            //you can retry or cancel the operation.\n            e.printStackTrace();\n        }\n\n        return order.getMerchantOrderNo();\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/TradeOrderServiceProxy.java",
    "content": "package io.tyloo.sample.http.order.service;\n\nimport io.tyloo.sample.http.capital.api.CapitalTradeOrderService;\nimport io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;\nimport io.tyloo.sample.http.redpacket.api.RedPacketTradeOrderService;\nimport io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.Propagation;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.context.MethodTransactionContextEditor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class TradeOrderServiceProxy {\n\n    @Autowired\n    CapitalTradeOrderService capitalTradeOrderService;\n\n    @Autowired\n    RedPacketTradeOrderService redPacketTradeOrderService;\n\n    /*the propagation need set Propagation.SUPPORTS,otherwise the recover doesn't work,\n      The default value is Propagation.REQUIRED, which means will begin new transaction when recover.\n    */\n    @Tyloo(propagation = Propagation.SUPPORTS, confirmMethod = \"record\", cancelMethod = \"record\", transactionContextEditor = MethodTransactionContextEditor.class)\n    public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {\n        return capitalTradeOrderService.record(transactionContext, tradeOrderDto);\n    }\n\n    @Tyloo(propagation = Propagation.SUPPORTS, confirmMethod = \"record\", cancelMethod = \"record\", transactionContextEditor = MethodTransactionContextEditor.class)\n    public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {\n        return redPacketTradeOrderService.record(transactionContext, tradeOrderDto);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/web/controller/OrderController.java",
    "content": "package io.tyloo.sample.http.order.web.controller;\n\nimport io.tyloo.sample.http.order.service.AccountServiceImpl;\nimport io.tyloo.sample.http.order.service.PlaceOrderServiceImpl;\nimport io.tyloo.sample.http.order.web.controller.vo.PlaceOrderRequest;\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.entity.Product;\nimport io.tyloo.sample.order.domain.repository.ProductRepository;\nimport io.tyloo.sample.order.domain.service.OrderServiceImpl;\nimport org.apache.commons.lang3.tuple.ImmutablePair;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.view.RedirectView;\n\nimport java.math.BigDecimal;\nimport java.security.InvalidParameterException;\nimport java.util.List;\n\n\n@Controller\n@RequestMapping(\"\")\npublic class OrderController {\n\n    @Autowired\n    PlaceOrderServiceImpl placeOrderService;\n\n    @Autowired\n    ProductRepository productRepository;\n\n    @Autowired\n    AccountServiceImpl accountService;\n\n    @Autowired\n    OrderServiceImpl orderService;\n\n    @RequestMapping(value = \"/\", method = RequestMethod.GET)\n    public ModelAndView index() {\n        ModelAndView mv = new ModelAndView(\"/index\");\n        return mv;\n    }\n\n    @RequestMapping(value = \"/user/{userId}/shop/{shopId}\", method = RequestMethod.GET)\n    public ModelAndView getProductsInShop(@PathVariable long userId,\n                                          @PathVariable long shopId) {\n        List<Product> products = productRepository.findByShopId(shopId);\n\n        ModelAndView mv = new ModelAndView(\"/shop\");\n\n        mv.addObject(\"products\", products);\n        mv.addObject(\"userId\", userId);\n        mv.addObject(\"shopId\", shopId);\n\n        return mv;\n    }\n\n    @RequestMapping(value = \"/user/{userId}/shop/{shopId}/product/{productId}/confirm\", method = RequestMethod.GET)\n    public ModelAndView productDetail(@PathVariable long userId,\n                                      @PathVariable long shopId,\n                                      @PathVariable long productId) {\n\n        ModelAndView mv = new ModelAndView(\"product_detail\");\n\n        mv.addObject(\"capitalAmount\", accountService.getCapitalAccountByUserId(userId));\n        mv.addObject(\"redPacketAmount\", accountService.getRedPacketAccountByUserId(userId));\n\n        mv.addObject(\"product\", productRepository.findById(productId));\n\n        mv.addObject(\"userId\", userId);\n        mv.addObject(\"shopId\", shopId);\n\n        return mv;\n    }\n\n    @RequestMapping(value = \"/placeorder\", method = RequestMethod.POST)\n    public RedirectView placeOrder(@RequestParam String redPacketPayAmount,\n                                   @RequestParam long shopId,\n                                   @RequestParam long payerUserId,\n                                   @RequestParam long productId) {\n\n\n        PlaceOrderRequest request = buildRequest(redPacketPayAmount, shopId, payerUserId, productId);\n\n        String merchantOrderNo = placeOrderService.placeOrder(request.getPayerUserId(), request.getShopId(),\n                request.getProductQuantities(), request.getRedPacketPayAmount());\n\n        return new RedirectView(\"payresult/\" + merchantOrderNo);\n    }\n\n    @RequestMapping(value = \"/payresult/{merchantOrderNo}\", method = RequestMethod.GET)\n    public ModelAndView getPayResult(@PathVariable String merchantOrderNo) {\n\n        ModelAndView mv = new ModelAndView(\"pay_success\");\n\n        String payResultTip = null;\n\n        Order foundOrder = orderService.findOrderByMerchantOrderNo(merchantOrderNo);\n\n        payResultTip = foundOrder.getStatus();\n\n        mv.addObject(\"payResult\", payResultTip);\n\n        mv.addObject(\"capitalAmount\", accountService.getCapitalAccountByUserId(foundOrder.getPayerUserId()));\n        mv.addObject(\"redPacketAmount\", accountService.getRedPacketAccountByUserId(foundOrder.getPayerUserId()));\n\n        return mv;\n    }\n\n\n    private PlaceOrderRequest buildRequest(String redPacketPayAmount, long shopId, long payerUserId, long productId) {\n        BigDecimal redPacketPayAmountInBigDecimal = new BigDecimal(redPacketPayAmount);\n        if (redPacketPayAmountInBigDecimal.compareTo(BigDecimal.ZERO) < 0) {\n            throw new InvalidParameterException(\"invalid red packet amount :\" + redPacketPayAmount);\n        }\n\n        PlaceOrderRequest request = new PlaceOrderRequest();\n        request.setPayerUserId(payerUserId);\n        request.setShopId(shopId);\n        request.setRedPacketPayAmount(new BigDecimal(redPacketPayAmount));\n        request.getProductQuantities().add(new ImmutablePair<Long, Integer>(productId, 1));\n        return request;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/web/controller/vo/PlaceOrderRequest.java",
    "content": "package io.tyloo.sample.http.order.web.controller.vo;\n\nimport org.apache.commons.lang3.tuple.Pair;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic class PlaceOrderRequest {\n\n    private long payerUserId;\n\n    private long shopId;\n\n    private BigDecimal redPacketPayAmount;\n\n    private List<Pair<Long, Integer>> productQuantities = new ArrayList<Pair<Long, Integer>>();\n\n    public long getPayerUserId() {\n        return payerUserId;\n    }\n\n    public void setPayerUserId(long payerUserId) {\n        this.payerUserId = payerUserId;\n    }\n\n    public long getShopId() {\n        return shopId;\n    }\n\n    public void setShopId(long shopId) {\n        this.shopId = shopId;\n    }\n\n    public BigDecimal getRedPacketPayAmount() {\n        return redPacketPayAmount;\n    }\n\n    public void setRedPacketPayAmount(BigDecimal redPacketPayAmount) {\n        this.redPacketPayAmount = redPacketPayAmount;\n    }\n\n    public List<Pair<Long, Integer>> getProductQuantities() {\n        return productQuantities;\n    }\n\n    public void setProductQuantities(List<Pair<Long, Integer>> productQuantities) {\n        this.productQuantities = productQuantities;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/config/spring/local/appcontext-service-consumer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <bean id=\"httpInvokerRequestExecutor\"\n          class=\"org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor\">\n        <property name=\"connectTimeout\" value=\"2000\"/>\n        <property name=\"readTimeout\" value=\"2000\"/>\n    </bean>\n\n    <bean id=\"captialTradeOrderService\" class=\"org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean\">\n        <property name=\"serviceUrl\" value=\"http://localhost:8090/remoting/CapitalTradeOrderService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.capital.api.CapitalTradeOrderService\"/>\n        <property name=\"httpInvokerRequestExecutor\" ref=\"httpInvokerRequestExecutor\"/>\n    </bean>\n\n    <bean id=\"capitalAccountService\" class=\"org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean\">\n        <property name=\"serviceUrl\" value=\"http://localhost:8090/remoting/CapitalAccountService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.capital.api.CapitalAccountService\"/>\n        <property name=\"httpInvokerRequestExecutor\" ref=\"httpInvokerRequestExecutor\"/>\n    </bean>\n\n    <bean id=\"redPacketAccountService\" class=\"org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean\">\n        <property name=\"serviceUrl\" value=\"http://localhost:8091/remoting/RedPacketAccountService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.redpacket.api.RedPacketAccountService\"/>\n        <property name=\"httpInvokerRequestExecutor\" ref=\"httpInvokerRequestExecutor\"/>\n    </bean>\n\n    <bean id=\"redPacketTradeOrderService\" class=\"org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean\">\n        <property name=\"serviceUrl\" value=\"http://localhost:8091/remoting/RedPacketTradeOrderService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.redpacket.api.RedPacketTradeOrderService\"/>\n        <property name=\"httpInvokerRequestExecutor\" ref=\"httpInvokerRequestExecutor\"/>\n    </bean>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/config/spring/local/appcontext-service-tcc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <util:properties id=\"tccjdbc\" location=\"classpath:tccjdbc.properties\"/>\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"30\"/>\n        <property name=\"recoverDuration\" value=\"5\"/>\n        <property name=\"cronExpression\" value=\"0/5 * * * * ?\"/>\n    </bean>\n\n    <bean id=\"transactionRepository\" class=\"io.tyloo.spring.repository.SpringJdbcTransactionRepository\">\n    <property name=\"dataSource\" ref=\"tylooTccDataSource\"/>\n    <property name=\"domain\" value=\"ORDER\"/>\n    <property name=\"tbSuffix\" value=\"_ORD\"/>\n    <property name=\"serializer\" ref=\"objectSerializer\"/>\n    </bean>\n\n    <bean id=\"tylooTccDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n    destroy-method=\"close\" lazy-init=\"false\">\n    <property name=\"driverClass\" value=\"#{tccjdbc['jdbc.driverClassName']}\"/>\n    <property name=\"jdbcUrl\" value=\"#{tccjdbc['tcc.jdbc.url']}\"/>\n    <property name=\"user\" value=\"#{tccjdbc['jdbc.username']}\"/>\n    <property name=\"password\" value=\"#{tccjdbc['jdbc.password']}\"/>\n    <property name=\"initialPoolSize\" value=\"#{tccjdbc['c3p0.initialPoolSize']}\"/>\n    <property name=\"minPoolSize\" value=\"#{tccjdbc['c3p0.minPoolSize']}\"/>\n    <property name=\"maxPoolSize\" value=\"#{tccjdbc['c3p0.maxPoolSize']}\"/>\n    <property name=\"acquireIncrement\" value=\"#{tccjdbc['c3p0.acquireIncrement']}\"/>\n    <property name=\"maxIdleTime\" value=\"#{tccjdbc['c3p0.maxIdleTime']}\"/>\n    <property name=\"checkoutTimeout\" value=\"#{tccjdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n\n    <!--<bean id=\"transactionRepository\" class=\"FileSystemTransactionRepository\">-->\n    <!--<property name=\"rootPath\" value=\"/data/tcc\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"transactionRepository\"-->\n    <!--class=\"io.tyloo.repository.ZooKeeperTransactionRepository\">-->\n    <!--&lt;!&ndash;<property name=\"zkServers\" value=\"localhost:2181,localhost:2183,localhost:2185\"/>&ndash;&gt;-->\n    <!--<property name=\"zkServers\" value=\"localhost:2181\"/>-->\n    <!--<property name=\"zkTimeout\" value=\"10000\"/>-->\n    <!--<property name=\"zkRootPath\" value=\"/tcc_ut\"/>-->\n    <!--</bean>-->\n\n    <bean id=\"objectSerializer\" class=\"io.tyloo.serializer.KryoPoolSerializer\"/>\n\n<!--     <bean id=\"transactionRepository\" class=\"RedisTransactionRepository\">\n        <property name=\"keyPrefix\" value=\"TCC:ORD:\"/>\n        <property name=\"jedisPool\" ref=\"jedisPool\"/>\n        <property name=\"serializer\" ref=\"kryoSerializer\"/>\n    </bean>\n\n    <bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">\n        <property name=\"maxTotal\" value=\"1000\"/>\n        <property name=\"maxWaitMillis\" value=\"1000\"/>\n    </bean>\n\n    <bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">\n        <constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>\n        <constructor-arg index=\"1\" value=\"127.0.0.1\"/>\n        <constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>\n        <constructor-arg index=\"3\" value=\"3000\" type=\"int\"/>\n        <constructor-arg index=\"4\" type=\"java.lang.String\">\n            <null/>\n        </constructor-arg>\n        <constructor-arg index=\"5\" value=\"0\" type=\"int\"/>\n    </bean> -->\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/config/spring/local/appcontext-web-servlet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd\">\n\n       <mvc:annotation-driven/>\n       <context:component-scan base-package=\"io.tyloo.sample.http\"/>\n       <aop:aspectj-autoproxy proxy-target-class=\"true\"/>\n\n\n       <bean id=\"contentNegotiationManager\"\n             class=\"org.springframework.web.accept.ContentNegotiationManagerFactoryBean\">\n              <property name=\"favorParameter\" value=\"false\"/>\n              <property name=\"ignoreAcceptHeader\" value=\"true\"/>\n              <property name=\"favorPathExtension\" value=\"false\"/>\n              <property name=\"mediaTypes\">\n                     <value>\n                            json=application/json\n                            html=text/html\n                     </value>\n              </property>\n              <property name=\"defaultContentType\" value=\"text/html\"/>\n       </bean>\n\n       <bean class=\"org.springframework.web.servlet.view.ContentNegotiatingViewResolver\">\n              <property name=\"contentNegotiationManager\" ref=\"contentNegotiationManager\"/>\n              <property name=\"order\" value=\"1\"/>\n              <property name=\"viewResolvers\">\n                     <list>\n                            <!-- ===================================================== -->\n                            <!-- ViewResolver For FreeMarker -->\n                            <!-- ===================================================== -->\n                            <bean id=\"freemarkerResolver\"\n                                  class=\"org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver\">\n\n                                   <property name=\"suffix\" value=\".ftl\"/>\n                                   <property name=\"contentType\" value=\"text/html;charset=utf-8\"/>\n                                   <property name=\"viewClass\">\n                                          <value>org.springframework.web.servlet.view.freemarker.FreeMarkerView</value>\n                                   </property>\n                                   <!--<property name=\"attributesMap\">\n                                       <map>\n                                           <entry key=\"truncate\"><bean class=\"com.rj8g.linkage.renba.web.TruncateTemplateMethodModel\" /></entry>\n                                       </map>\n                                   </property>-->\n                                   <property name=\"requestContextAttribute\" value=\"request\"/>\n                            </bean>\n                     </list>\n              </property>\n\n              <!--<property name=\"defaultViews\">-->\n                     <!--<list>-->\n                            <!--<bean class=\"org.springframework.web.servlet.view.json.MappingJackson2JsonView\"/>-->\n                     <!--</list>-->\n              <!--</property>-->\n       </bean>\n\n       <bean id=\"freemarkerConfig\" class=\"org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer\">\n              <property name=\"templateLoaderPath\">\n                     <value>/WEB-INF/ftl/</value>\n              </property>\n              <property name=\"freemarkerSettings\"><!-- 设置FreeMarker环境属性 -->\n                     <props>\n                            <prop key=\"template_update_delay\">5</prop>\n                            <!--刷新模板的周期，单位为秒 -->\n                            <prop key=\"default_encoding\">UTF-8</prop>\n                            <!--模板的编码格式 -->\n                            <prop key=\"locale\">UTF-8</prop>\n                            <!-- 本地化设置 -->\n                            <prop key=\"datetime_format\">yyyy-MM-dd HH:mm:ss</prop>\n                            <prop key=\"time_format\">HH:mm:ss</prop>\n                            <prop key=\"number_format\">0.####</prop>\n                            <prop key=\"boolean_format\">true,false</prop>\n                            <prop key=\"whitespace_stripping\">true</prop>\n                            <prop key=\"tag_syntax\">auto_detect</prop>\n                            <prop key=\"url_escaping_charset\">UTF-8</prop>\n                            <prop key=\"classic_compatible\">true</prop>\n                     </props>\n              </property>\n\n              <property name=\"freemarkerVariables\">\n                     <map>\n                            <entry key=\"xml_escape\" value-ref=\"fmXmlEscape\"/>\n                            <entry key=\"html_escape\" value-ref=\"fmHtmlEscape\"/>\n                     </map>\n              </property>\n\n       </bean>\n\n       <bean id=\"fmXmlEscape\" class=\"freemarker.template.utility.XmlEscape\"/>\n       <bean id=\"fmHtmlEscape\" class=\"freemarker.template.utility.HtmlEscape\"/>\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-http-order]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/sample-dubbo-order.properties",
    "content": "\n\nzookeeper.address=127.0.0.1:2181\nzookeeper.session.timeout=1800000\nzookeeper.connect.timeout=30000"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/tccjdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_ORD?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=300000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/index.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>tcc transacton dubbo sample order</title>\n</head>\n<body>\n<div class=\"page\">\n    <b>sample说明：</b>\n    <br/>\n    打开下面商品列表链接，选择一个商品购买，输入红包支付金额，进行支付，系统将使用红包＋资金账户转账支付。\n    <br/>\n    支付成功后，各个project会打印如下日志：\n    <br/>\n    <br>\n    &nbsp;&nbsp; <b>sample-dubbo-order:</b>\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; order try make payment called\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; order confirm make payment called\n    <br/>\n    <br/>\n    &nbsp;&nbsp; <b>sample-dubbo-capital:</b>\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; capital try record called\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; capital confirm record called\n    <br/>\n    <br/>\n    &nbsp;&nbsp; <b>sample-dubbo-redpacket:</b>\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; red packet try record called\n    <br/>\n    &nbsp;&nbsp;&nbsp;&nbsp; red packet confirm record called\n    <p/>\n    <a href=\"${request.contextPath}/user/2000/shop/1\">\n        商品列表链接\n    </a>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/pay_success.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>支付结果</title>\n</head>\n<body>\n<div class=\"page\">\n    <p>支付状态:<b>${payResult} </b> </p>\n    <p>剩余可用账户余额: ${capitalAmount?string(\"0.00\")}元</p>\n    <p>剩余可用红包余额: ${redPacketAmount?string(\"0.00\")}元</p>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/product_detail.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>订单详情</title>\n</head>\n<body>\n<div class=\"page\">\n    <p>名称: ${product.productName}</p>\n    <p>价格: ${product.price?string(\"0.00\")}元</p>\n\n    <p>可用账户余额: ${capitalAmount?string(\"0.00\")}元</p>\n    <p>可用红包余额: ${redPacketAmount?string(\"0.00\")}元</p>\n\n    <form action=\"${request.contextPath}/placeorder\" method=\"post\">\n        红包金额:&nbsp;&nbsp;&nbsp;<input type=\"text\" style=\"width: 220px\" name=\"redPacketPayAmount\" value=\"\" placeholder=\"请输入期望使用的红包金额\"/>\n        <input type=\"hidden\" name=\"shopId\" value=\"${shopId}\" />\n        <input type=\"hidden\" name=\"productId\" value=\"${product.productId}\"/>\n        <input type=\"hidden\" name=\"payerUserId\" value=\"${userId}\"/>\n\n        <input type=\"submit\" value=\"支付\"/>\n    </form>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/shop.ftl",
    "content": "[#ftl ]\n\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>商品列表</title>\n</head>\n<body>\n<div class=\"page\">\n    <div class=\"bg-f\">\n        <ul class=\"list\" >\n        [#if products?size > 0]\n            [#list products as product]\n                <li class=\"list-item\">\n                    <p>${product.productName}(${product.price?string(\"0.00\")})&nbsp;&nbsp;&nbsp;&nbsp;<span><a href=\"${request.contextPath}/user/${userId}/shop/${shopId}/product/${product.productId}/confirm\">购买</a></span></p>\n                </li>\n            [/#list]\n        [/#if]\n        </ul>\n    </div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/web.xml",
    "content": "<!DOCTYPE web-app PUBLIC\n        \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\n        \"http://java.sun.com/dtd/web-app_2_3.dtd\" >\n\n<web-app>\n    <display-name>Sample Http Order</display-name>\n    \n    <context-param>\n        <param-name>webAppRootKey</param-name>\n        <param-value>tyloo-http-order</param-value>\n    </context-param>\n    \n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml\n        </param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jConfigLocation</param-name>\n        <param-value>classpath:log/log4j.xml</param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jRefreshInterval</param-name>\n        <param-value>60000</param-value>\n    </context-param>\n\n    <listener>\n        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n    </listener>\n\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n\n    <filter>\n        <filter-name>characterEncodingFilter</filter-name>\n        <display-name>Character Encoding Filter</display-name>\n        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>\n        <init-param>\n            <param-name>encoding</param-name>\n            <param-value>UTF-8</param-value>\n        </init-param>\n        <init-param>\n            <param-name>forceEncoding</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>characterEncodingFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n    </filter-mapping>\n\n    <servlet>\n        <servlet-name>springmvc</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>classpath:config/spring/local/appcontext-web-servlet.xml</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>springmvc</servlet-name>\n        <url-pattern>/</url-pattern>\n    </servlet-mapping>\n\n</web-app>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-http-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-http-redpacket</artifactId>\n    <packaging>war</packaging>\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-http-redpacket-api</artifactId>\n           <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-redpacket</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webmvc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/java/io/tyloo/sample/http/redpacket/service/RedPacketAccountServiceImpl.java",
    "content": "package io.tyloo.sample.http.redpacket.service;\n\nimport io.tyloo.sample.http.redpacket.api.RedPacketAccountService;\nimport io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.math.BigDecimal;\n\n\npublic class RedPacketAccountServiceImpl implements RedPacketAccountService {\n\n    @Autowired\n    RedPacketAccountRepository redPacketAccountRepository;\n\n    @Override\n    public BigDecimal getRedPacketAccountByUserId(long userId) {\n        return redPacketAccountRepository.findByUserId(userId).getBalanceAmount();\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/java/io/tyloo/sample/http/redpacket/service/RedPacketTradeOrderServiceImpl.java",
    "content": "package io.tyloo.sample.http.redpacket.service;\n\nimport io.tyloo.sample.http.redpacket.api.RedPacketTradeOrderService;\nimport io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;\nimport io.tyloo.sample.redpacket.domain.entity.TradeOrder;\nimport io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;\nimport io.tyloo.sample.redpacket.domain.repository.TradeOrderRepository;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.context.MethodTransactionContextEditor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataIntegrityViolationException;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Calendar;\n\n\npublic class RedPacketTradeOrderServiceImpl implements RedPacketTradeOrderService {\n\n    @Autowired\n    RedPacketAccountRepository redPacketAccountRepository;\n\n    @Autowired\n    TradeOrderRepository tradeOrderRepository;\n\n    @Override\n    @Tyloo(confirmMethod = \"confirmRecord\", cancelMethod = \"cancelRecord\", transactionContextEditor = MethodTransactionContextEditor.class)\n    @Transactional\n    public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"red packet try record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        //check if the trade order has need recorded.\n        //if record, then this method call return success directly.\n        if (foundTradeOrder == null) {\n\n            TradeOrder tradeOrder = new TradeOrder(\n                    tradeOrderDto.getSelfUserId(),\n                    tradeOrderDto.getOppositeUserId(),\n                    tradeOrderDto.getMerchantOrderNo(),\n                    tradeOrderDto.getAmount()\n            );\n\n            try {\n                tradeOrderRepository.insert(tradeOrder);\n\n                RedPacketAccount transferFromAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n                transferFromAccount.transferFrom(tradeOrderDto.getAmount());\n\n                redPacketAccountRepository.save(transferFromAccount);\n            } catch (DataIntegrityViolationException e) {\n\n            }\n        }\n\n        return \"success\";\n    }\n\n    @Transactional\n    public void confirmRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"red packet confirm record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        if (null != tradeOrder && \"DRAFT\".equals(tradeOrder.getStatus())) {\n            tradeOrder.confirm();\n            tradeOrderRepository.update(tradeOrder);\n\n            RedPacketAccount transferToAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());\n\n            transferToAccount.transferTo(tradeOrderDto.getAmount());\n\n            redPacketAccountRepository.save(transferToAccount);\n        }\n    }\n\n    @Transactional\n    public void cancelRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {\n\n        try {\n            Thread.sleep(1000l);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        System.out.println(\"red packet cancel record called. time seq:\" + DateFormatUtils.format(Calendar.getInstance(), \"yyyy-MM-dd HH:mm:ss\"));\n\n        TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());\n\n        if (null != tradeOrder && \"DRAFT\".equals(tradeOrder.getStatus())) {\n            tradeOrder.cancel();\n            tradeOrderRepository.update(tradeOrder);\n\n            RedPacketAccount capitalAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());\n\n            capitalAccount.cancelTransfer(tradeOrderDto.getAmount());\n\n            redPacketAccountRepository.save(capitalAccount);\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/config/spring/local/appcontext-service-provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <bean name=\"redPacketAccountService\"\n          class=\"io.tyloo.sample.http.redpacket.service.RedPacketAccountServiceImpl\"/>\n\n    <bean name=\"redPacketTradeOrderService\"\n          class=\"io.tyloo.sample.http.redpacket.service.RedPacketTradeOrderServiceImpl\"/>\n\n    <bean name=\"redPacketAccountServiceExporter\"\n          class=\"org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter\">\n        <property name=\"service\" ref=\"redPacketAccountService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.redpacket.api.RedPacketAccountService\"/>\n    </bean>\n\n    <bean name=\"redPacketTradeOrderServiceExporter\"\n          class=\"org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter\">\n        <property name=\"service\" ref=\"redPacketTradeOrderService\"/>\n        <property name=\"serviceInterface\"\n                  value=\"io.tyloo.sample.http.redpacket.api.RedPacketTradeOrderService\"/>\n    </bean>\n\n    <bean id=\"httpServer\"\n          class=\"org.springframework.remoting.support.SimpleHttpServerFactoryBean\">\n        <property name=\"contexts\">\n            <util:map>\n                <entry key=\"/remoting/RedPacketTradeOrderService\" value-ref=\"redPacketTradeOrderServiceExporter\"/>\n                <entry key=\"/remoting/RedPacketAccountService\" value-ref=\"redPacketAccountServiceExporter\"/>\n            </util:map>\n        </property>\n        <property name=\"port\" value=\"8091\"/>\n    </bean>\n\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/config/spring/local/appcontext-service-tcc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample.http\"/>\n\n    <util:properties id=\"tccjdbc\" location=\"classpath:tccjdbc.properties\"/>\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"5\"/>\n        <property name=\"recoverDuration\" value=\"60\"/>\n        <property name=\"cronExpression\" value=\"0/30 * * * * ?\"/>\n    </bean>\n\n\n    <bean id=\"transactionRepository\" class=\"io.tyloo.spring.repository.SpringJdbcTransactionRepository\">\n    <property name=\"dataSource\" ref=\"tylooTccDataSource\"/>\n    <property name=\"domain\" value=\"REDPACKET\"/>\n    <property name=\"tbSuffix\" value=\"_RED\"/>\n    </bean>\n\n    <bean id=\"tylooTccDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{tccjdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{tccjdbc['tcc.jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{tccjdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{tccjdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{tccjdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{tccjdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{tccjdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{tccjdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{tccjdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{tccjdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <bean id=\"kryoSerializer\" class=\"io.tyloo.serializer.KryoPoolSerializer\"/>\n\n<!--     <bean id=\"transactionRepository\" class=\"RedisTransactionRepository\">\n        <property name=\"keyPrefix\" value=\"TCC:RED:\"/>\n        <property name=\"jedisPool\" ref=\"jedisPool\"/>\n        <property name=\"serializer\" ref=\"kryoSerializer\"/>\n    </bean>\n\n    <bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">\n        <property name=\"maxTotal\" value=\"300\"/>\n        <property name=\"maxWaitMillis\" value=\"3000\"/>\n    </bean>\n\n    <bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">\n        <constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>\n        <constructor-arg index=\"1\" value=\"127.0.0.1\"/>\n        <constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>\n        <constructor-arg index=\"3\" value=\"3000\" type=\"int\"/>\n        <constructor-arg index=\"4\" type=\"java.lang.String\">\n            <null/>\n        </constructor-arg>\n        <constructor-arg index=\"5\" value=\"0\" type=\"int\"/>\n    </bean> -->\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/config/spring/local/appcontext-web-servlet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">\n       \n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-http-redpacket]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/tccjdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_RED?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=30000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/webapp/WEB-INF/web.xml",
    "content": "<!DOCTYPE web-app PUBLIC\n        \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\"\n        \"http://java.sun.com/dtd/web-app_2_3.dtd\" >\n\n<web-app>\n    <display-name>Sample Dubbo Redpacket</display-name>\n\n    <context-param>\n        <param-name>webAppRootKey</param-name>\n        <param-value>tyloo-http-redpacket</param-value>\n    </context-param>\n    \n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>\n            classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml\n        </param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jConfigLocation</param-name>\n        <param-value>classpath:log/log4j.xml</param-value>\n    </context-param>\n\n    <context-param>\n        <param-name>log4jRefreshInterval</param-name>\n        <param-value>60000</param-value>\n    </context-param>\n\n    <listener>\n        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n    </listener>\n\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n\n    <filter>\n        <filter-name>characterEncodingFilter</filter-name>\n        <display-name>Character Encoding Filter</display-name>\n        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>\n        <init-param>\n            <param-name>encoding</param-name>\n            <param-value>UTF-8</param-value>\n        </init-param>\n        <init-param>\n            <param-name>forceEncoding</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>characterEncodingFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n    </filter-mapping>\n\n    <servlet>\n        <servlet-name>springmvc</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>classpath:config/spring/local/appcontext-web-servlet.xml</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>springmvc</servlet-name>\n        <url-pattern>/</url-pattern>\n    </servlet-mapping>\n\n</web-app>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/webapp/index.jsp",
    "content": "hello tcc transacton http sample red packet"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-http-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n       <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-http-redpacket-api</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-api</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/src/main/java/io/tyloo/sample/http/redpacket/api/RedPacketAccountService.java",
    "content": "package io.tyloo.sample.http.redpacket.api;\n\nimport java.math.BigDecimal;\n\n\npublic interface RedPacketAccountService {\n    BigDecimal getRedPacketAccountByUserId(long userId);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/src/main/java/io/tyloo/sample/http/redpacket/api/RedPacketTradeOrderService.java",
    "content": "package io.tyloo.sample.http.redpacket.api;\n\nimport io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;\nimport io.tyloo.api.TransactionContext;\n\n\npublic interface RedPacketTradeOrderService {\n\n    public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/src/main/java/io/tyloo/sample/http/redpacket/api/dto/RedPacketTradeOrderDto.java",
    "content": "package io.tyloo.sample.http.redpacket.api.dto;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\npublic class RedPacketTradeOrderDto implements Serializable {\n\n    private static final long serialVersionUID = 4747014387277477558L;\n\n    private long selfUserId;\n\n    private long oppositeUserId;\n\n    private String orderTitle;\n\n    private String merchantOrderNo;\n\n    private BigDecimal amount;\n\n    public long getSelfUserId() {\n        return selfUserId;\n    }\n\n    public void setSelfUserId(long selfUserId) {\n        this.selfUserId = selfUserId;\n    }\n\n    public long getOppositeUserId() {\n        return oppositeUserId;\n    }\n\n    public void setOppositeUserId(long oppositeUserId) {\n        this.oppositeUserId = oppositeUserId;\n    }\n\n    public String getOrderTitle() {\n        return orderTitle;\n    }\n\n    public void setOrderTitle(String orderTitle) {\n        this.orderTitle = orderTitle;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public void setMerchantOrderNo(String merchantOrderNo) {\n        this.merchantOrderNo = merchantOrderNo;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-tutorial-sample</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-sample-domain</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>tyloo-sample-common</module>\n        <module>tyloo-sample-order</module>\n        <module>tyloo-sample-captial</module>\n        <module>tyloo-sample-redpacket</module>\n    </modules>\n\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-sample-domain</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-sample-captial</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-common</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/entity/CapitalAccount.java",
    "content": "package io.tyloo.sample.capital.domain.entity;\n\n\n\nimport io.tyloo.sample.exception.InsufficientBalanceException;\n\nimport java.math.BigDecimal;\n\n\npublic class CapitalAccount {\n\n    private long id;\n\n    private long userId;\n\n    private BigDecimal balanceAmount;\n\n    private BigDecimal transferAmount = BigDecimal.ZERO;\n\n    public long getUserId() {\n        return userId;\n    }\n\n    public BigDecimal getBalanceAmount() {\n        return balanceAmount;\n    }\n\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public void transferFrom(BigDecimal amount) {\n\n        this.balanceAmount = this.balanceAmount.subtract(amount);\n\n        if (BigDecimal.ZERO.compareTo(this.balanceAmount) > 0) {\n            throw new InsufficientBalanceException();\n        }\n\n        transferAmount = transferAmount.add(amount.negate());\n    }\n\n    public void transferTo(BigDecimal amount) {\n        this.balanceAmount = this.balanceAmount.add(amount);\n        transferAmount = transferAmount.add(amount);\n    }\n\n    public void cancelTransfer(BigDecimal amount) {\n        transferTo(amount);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/entity/TradeOrder.java",
    "content": "package io.tyloo.sample.capital.domain.entity;\n\nimport java.math.BigDecimal;\n\npublic class TradeOrder {\n\n    private long id;\n\n    private long selfUserId;\n\n    private long oppositeUserId;\n\n    private String merchantOrderNo;\n\n    private BigDecimal amount;\n\n    private String status = \"DRAFT\";\n\n    private long version = 1l;\n\n    public TradeOrder() {\n    }\n\n    public TradeOrder(long selfUserId, long oppositeUserId, String merchantOrderNo, BigDecimal amount) {\n        this.selfUserId = selfUserId;\n        this.oppositeUserId = oppositeUserId;\n        this.merchantOrderNo = merchantOrderNo;\n        this.amount = amount;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public long getSelfUserId() {\n        return selfUserId;\n    }\n\n    public long getOppositeUserId() {\n        return oppositeUserId;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void confirm() {\n        this.status = \"CONFIRM\";\n    }\n\n    public void cancel() {\n        this.status = \"CANCEL\";\n    }\n\n    public long getVersion() {\n        return version;\n    }\n\n    public void updateVersion() {\n        this.version = version + 1;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/repository/CapitalAccountRepository.java",
    "content": "package io.tyloo.sample.capital.domain.repository;\n\nimport io.tyloo.sample.capital.domain.entity.CapitalAccount;\nimport io.tyloo.sample.capital.infrastructure.dao.CapitalAccountDao;\nimport io.tyloo.sample.exception.InsufficientBalanceException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Repository;\n\n\n@Repository\npublic class CapitalAccountRepository {\n\n    @Autowired\n    CapitalAccountDao capitalAccountDao;\n\n    public CapitalAccount findByUserId(long userId) {\n\n        return capitalAccountDao.findByUserId(userId);\n    }\n\n    public void save(CapitalAccount capitalAccount) {\n        int effectCount = capitalAccountDao.update(capitalAccount);\n        if (effectCount < 1) {\n            throw new InsufficientBalanceException();\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/repository/TradeOrderRepository.java",
    "content": "package io.tyloo.sample.capital.domain.repository;\n\nimport io.tyloo.sample.capital.domain.entity.TradeOrder;\nimport io.tyloo.sample.capital.infrastructure.dao.TradeOrderDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.OptimisticLockingFailureException;\nimport org.springframework.stereotype.Repository;\n\n\n@Repository\npublic class TradeOrderRepository {\n\n    @Autowired\n    TradeOrderDao tradeOrderDao;\n\n    public void insert(TradeOrder tradeOrder) {\n        tradeOrderDao.insert(tradeOrder);\n    }\n\n    public void update(TradeOrder tradeOrder) {\n        tradeOrder.updateVersion();\n        int effectCount = tradeOrderDao.update(tradeOrder);\n        if (effectCount < 1) {\n            throw new OptimisticLockingFailureException(\"update trade order failed\");\n        }\n    }\n\n    public TradeOrder findByMerchantOrderNo(String merchantOrderNo) {\n        return tradeOrderDao.findByMerchantOrderNo(merchantOrderNo);\n    }\n\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/infrastructure/dao/CapitalAccountDao.java",
    "content": "package io.tyloo.sample.capital.infrastructure.dao;\n\nimport io.tyloo.sample.capital.domain.entity.CapitalAccount;\n\n\npublic interface CapitalAccountDao {\n\n    CapitalAccount findByUserId(long userId);\n\n    int update(CapitalAccount capitalAccount);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/infrastructure/dao/TradeOrderDao.java",
    "content": "package io.tyloo.sample.capital.infrastructure.dao;\n\n\nimport io.tyloo.sample.capital.domain.entity.TradeOrder;\n\n\npublic interface TradeOrderDao {\n\n    int insert(TradeOrder tradeOrder);\n\n    int update(TradeOrder tradeOrder);\n\n    TradeOrder findByMerchantOrderNo(String merchantOrderNo);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/spring/local/appcontext-service-dao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:jdbc=\"http://www.springframework.org/schema/jdbc\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample\"/>\n\n    <bean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\">\n        <property name=\"dataSource\" ref=\"capitalDataSource\"/>\n        <property name=\"mapperLocations\" value=\"classpath*:config/sqlmap/main/sample-*.xml\"/>\n    </bean>\n\n    <bean id=\"capitalAccountDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.capital.infrastructure.dao.CapitalAccountDao\"></property>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"></property>\n    </bean>\n\n    <bean id=\"tradeOrderDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.capital.infrastructure.dao.TradeOrderDao\"/>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/>\n    </bean>\n\n    <jdbc:embedded-database id=\"capitalDataSource\"/>\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/spring/local/appcontext-service-datasource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd\n         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <util:properties id=\"jdbc\" location=\"classpath:jdbc.properties\"/>\n\n    <bean id=\"capitalDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{jdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{jdbc['jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{jdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{jdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{jdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{jdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{jdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{jdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{jdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{jdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <bean id=\"transactionManager\"\n          class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n        <property name=\"dataSource\" ref=\"capitalDataSource\"/>\n    </bean>\n\n    <tx:annotation-driven transaction-manager=\"transactionManager\"/>\n\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/sqlmap/main/sample-capitalaccount.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.capital.infrastructure.dao.CapitalAccountDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.capital.domain.entity.CapitalAccount\">\n        <id property=\"id\" javaType=\"java.lang.Long\" column=\"CAPITAL_ACCOUNT_ID\"/>\n        <result property=\"balanceAmount\" column=\"BALANCE_AMOUNT\"/>\n        <result property=\"userId\" column=\"USER_ID\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        CAPITAL_ACCOUNT_ID,\n        BALANCE_AMOUNT,\n        USER_ID\n        FROM\n        CAP_CAPITAL_ACCOUNT\n    </sql>\n\n\n    <select id=\"findAll\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n    </select>\n\n    <select id=\"findByUserId\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE USER_ID = #{id,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n\n    <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO CAP_CAPITAL_ACCOUNT\n        (\n        BALANCE_AMOUNT,\n        USER_ID\n        )\n        VALUES\n            (\n            #{balanceAmount},\n            #{userId}\n            )\n    </insert>\n\n    <update id=\"update\" parameterType=\"io.tyloo.sample.capital.domain.entity.CapitalAccount\">\n        UPDATE\n        CAP_CAPITAL_ACCOUNT\n        SET\n        BALANCE_AMOUNT = BALANCE_AMOUNT+#{transferAmount}\n        WHERE CAPITAL_ACCOUNT_ID = #{id} AND BALANCE_AMOUNT+#{transferAmount}>=0\n    </update>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/sqlmap/main/sample-tradeorder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.capital.infrastructure.dao.TradeOrderDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.capital.domain.entity.TradeOrder\">\n        <id property=\"id\" javaType=\"java.lang.Long\" column=\"ID\"/>\n        <result property=\"selfUserId\" column=\"SELF_USER_ID\"/>\n        <result property=\"oppositeUserId\" column=\"OPPOSITE_USER_ID\"/>\n        <result property=\"merchantOrderNo\" column=\"MERCHANT_ORDER_NO\"/>\n        <result property=\"amount\" column=\"AMOUNT\"/>\n        <result property=\"status\" column=\"STATUS\"/>\n        <result property=\"version\" column=\"VERSION\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        ID,\n        SELF_USER_ID,\n        OPPOSITE_USER_ID,\n        MERCHANT_ORDER_NO,\n        AMOUNT,\n        STATUS,\n        VERSION\n        FROM\n        CAP_TRADE_ORDER\n    </sql>\n\n    <select id=\"findByMerchantOrderNo\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE MERCHANT_ORDER_NO = #{merchantOrderNo}\n    </select>\n\n    <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\"\n            parameterType=\"io.tyloo.sample.capital.domain.entity.TradeOrder\">\n        INSERT INTO CAP_TRADE_ORDER\n        (\n        SELF_USER_ID,\n        OPPOSITE_USER_ID,\n        MERCHANT_ORDER_NO,\n        AMOUNT,\n        STATUS,\n        VERSION\n        )\n        VALUES\n        (\n        #{selfUserId},\n        #{oppositeUserId},\n        #{merchantOrderNo},\n        #{amount},\n        #{status},\n        #{version}\n        )\n    </insert>\n\n    <update id=\"update\" parameterType=\"io.tyloo.sample.capital.domain.entity.TradeOrder\">\n        UPDATE\n        CAP_TRADE_ORDER\n        SET\n        STATUS = #{status},\n        VERSION = #{version}\n        WHERE ID = #{id} AND VERSION = #{version}-1\n    </update>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/jdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_CAP?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\ntcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=30000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-capital]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-sample-domain</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-sample-common</artifactId>\n    <packaging>jar</packaging>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-common/src/main/java/io/tyloo/sample/exception/InsufficientBalanceException.java",
    "content": "package io.tyloo.sample.exception;\n\n\npublic class InsufficientBalanceException extends RuntimeException {\n    private static final long serialVersionUID = 6689953065473521009L;\n\n    public InsufficientBalanceException() {\n\n    }\n\n    public InsufficientBalanceException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-sample-domain</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-sample-order</artifactId>\n\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-common</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n            <version>5.2.5</version>\n            <scope>compile</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/Order.java",
    "content": "package io.tyloo.sample.order.domain.entity;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport cn.hutool.core.lang.UUID;\n\n\npublic class Order implements Serializable {\n\n    private static final long serialVersionUID = -5908730245224893590L;\n    private long id;\n\n    private long payerUserId;\n\n    private long payeeUserId;\n\n    private BigDecimal redPacketPayAmount;\n\n    private BigDecimal capitalPayAmount;\n\n    private String status = \"DRAFT\";\n\n    private String merchantOrderNo;\n\n    private long version = 1l;\n\n    private List<OrderLine> orderLines = new ArrayList<OrderLine>();\n\n    public Order() {\n\n    }\n\n    public Order(long payerUserId, long payeeUserId) {\n        this.payerUserId = payerUserId;\n        this.payeeUserId = payeeUserId;\n        this.merchantOrderNo = UUID.randomUUID().toString().replace(\"-\",\"\");\n    }\n\n    public long getPayerUserId() {\n        return payerUserId;\n    }\n\n    public long getPayeeUserId() {\n        return payeeUserId;\n    }\n\n    public BigDecimal getTotalAmount() {\n\n        BigDecimal totalAmount = BigDecimal.ZERO;\n\n        for (OrderLine orderLine : orderLines) {\n\n            totalAmount = totalAmount.add(orderLine.getTotalAmount());\n        }\n        return totalAmount;\n    }\n\n    public void addOrderLine(OrderLine orderLine) {\n        this.orderLines.add(orderLine);\n    }\n\n    public List<OrderLine> getOrderLines() {\n        return Collections.unmodifiableList(this.orderLines);\n    }\n\n    public void pay(BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {\n        this.redPacketPayAmount = redPacketPayAmount;\n        this.capitalPayAmount = capitalPayAmount;\n        this.status = \"PAYING\";\n    }\n\n    public BigDecimal getRedPacketPayAmount() {\n        return redPacketPayAmount;\n    }\n\n    public BigDecimal getCapitalPayAmount() {\n        return capitalPayAmount;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public void setMerchantOrderNo(String merchantOrderNo) {\n        this.merchantOrderNo = merchantOrderNo;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void confirm() {\n        this.status = \"CONFIRMED\";\n    }\n\n    public void cancelPayment() {\n        this.status = \"PAY_FAILED\";\n    }\n\n\n    public long getVersion() {\n        return version;\n    }\n\n    public void updateVersion() {\n        version++;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/OrderLine.java",
    "content": "package io.tyloo.sample.order.domain.entity;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\npublic class OrderLine implements Serializable {\n\n    private static final long serialVersionUID = 2300754647209250837L;\n    private long id;\n\n    private long productId;\n\n    private int quantity;\n\n    private BigDecimal unitPrice;\n\n    public OrderLine() {\n\n    }\n\n    public OrderLine(Long productId, Integer quantity,BigDecimal unitPrice) {\n        this.productId = productId;\n        this.quantity = quantity;\n        this.unitPrice = unitPrice;\n    }\n\n    public long getProductId() {\n        return productId;\n    }\n\n    public int getQuantity() {\n        return quantity;\n    }\n\n    public BigDecimal getUnitPrice() {\n        return unitPrice;\n    }\n\n    public BigDecimal getTotalAmount() {\n        return unitPrice.multiply(BigDecimal.valueOf(quantity));\n    }\n\n    public long getId() {\n        return id;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/Product.java",
    "content": "package io.tyloo.sample.order.domain.entity;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\n\n\npublic class Product implements Serializable{\n    private long productId;\n\n    private long shopId;\n\n    private String productName;\n\n    private BigDecimal price;\n\n    public Product() {\n    }\n\n    public Product(long productId, long shopId, String productName, BigDecimal price) {\n        this.productId = productId;\n        this.shopId = shopId;\n        this.productName = productName;\n        this.price = price;\n    }\n\n    public long getProductId() {\n        return productId;\n    }\n\n    public long getShopId() {\n        return shopId;\n    }\n\n    public String getProductName() {\n        return productName;\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/Shop.java",
    "content": "package io.tyloo.sample.order.domain.entity;\n\n\npublic class Shop {\n\n    private long id;\n\n    private long ownerUserId;\n\n    public long getOwnerUserId() {\n        return ownerUserId;\n    }\n\n    public long getId() {\n        return id;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/factory/OrderFactory.java",
    "content": "package io.tyloo.sample.order.domain.factory;\n\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.entity.OrderLine;\nimport io.tyloo.sample.order.domain.repository.ProductRepository;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n\n@Component\npublic class OrderFactory {\n\n    @Autowired\n    ProductRepository productRepository;\n\n    public Order buildOrder(long payerUserId, long payeeUserId, List<Pair<Long, Integer>> productQuantities) {\n\n        Order order = new Order(payerUserId, payeeUserId);\n\n        for (Pair<Long, Integer> pair : productQuantities) {\n            long productId = pair.getLeft();\n            order.addOrderLine(new OrderLine(productId, pair.getRight(), productRepository.findById(productId).getPrice()));\n        }\n\n        return order;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/repository/OrderRepository.java",
    "content": "package io.tyloo.sample.order.domain.repository;\n\n\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.entity.OrderLine;\nimport io.tyloo.sample.order.infrastructure.dao.OrderDao;\nimport io.tyloo.sample.order.infrastructure.dao.OrderLineDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.OptimisticLockingFailureException;\nimport org.springframework.stereotype.Repository;\n\n/*\n *\n * @Author:Zh1Cheung zh1cheunglq@gmail.com\n * @Date: 18:04 2019/4/19\n *\n */\n@Repository\npublic class OrderRepository {\n\n    @Autowired\n    OrderDao orderDao;\n\n    @Autowired\n    OrderLineDao orderLineDao;\n\n    public void createOrder(Order order) {\n        orderDao.insert(order);\n\n        for (OrderLine orderLine : order.getOrderLines()) {\n            orderLineDao.insert(orderLine);\n        }\n    }\n\n    public void updateOrder(Order order) {\n        order.updateVersion();\n        int effectCount = orderDao.update(order);\n\n        if (effectCount < 1) {\n            throw new OptimisticLockingFailureException(\"update order failed\");\n        }\n    }\n\n    public Order findByMerchantOrderNo(String merchantOrderNo) {\n        return orderDao.findByMerchantOrderNo(merchantOrderNo);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/repository/ProductRepository.java",
    "content": "package io.tyloo.sample.order.domain.repository;\n\nimport io.tyloo.sample.order.domain.entity.Product;\nimport io.tyloo.sample.order.infrastructure.dao.ProductDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n\n@Repository\npublic class ProductRepository {\n\n    @Autowired\n    ProductDao productDao;\n\n    public Product findById(long productId){\n        return productDao.findById(productId);\n    }\n\n    public List<Product> findByShopId(long shopId){\n        return productDao.findByShopId(shopId);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/repository/ShopRepository.java",
    "content": "package io.tyloo.sample.order.domain.repository;\n\nimport io.tyloo.sample.order.domain.entity.Shop;\nimport io.tyloo.sample.order.infrastructure.dao.ShopDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Repository;\n\n\n@Repository\npublic class ShopRepository {\n\n    @Autowired\n    ShopDao shopDao;\n\n    public Shop findById(long id) {\n\n        return shopDao.findById(id);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/service/OrderServiceImpl.java",
    "content": "package io.tyloo.sample.order.domain.service;\n\nimport io.tyloo.sample.order.domain.entity.Order;\nimport io.tyloo.sample.order.domain.factory.OrderFactory;\nimport io.tyloo.sample.order.domain.repository.OrderRepository;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\n\n\n@Service\npublic class OrderServiceImpl {\n\n    @Autowired\n    OrderRepository orderRepository;\n\n    @Autowired\n    OrderFactory orderFactory;\n\n    @Transactional\n    public Order createOrder(long payerUserId, long payeeUserId, List<Pair<Long, Integer>> productQuantities) {\n        Order order = orderFactory.buildOrder(payerUserId, payeeUserId, productQuantities);\n\n        orderRepository.createOrder(order);\n\n        return order;\n    }\n\n    public Order findOrderByMerchantOrderNo(String orderNo) {\n        return orderRepository.findByMerchantOrderNo(orderNo);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/OrderDao.java",
    "content": "package io.tyloo.sample.order.infrastructure.dao;\n\nimport io.tyloo.sample.order.domain.entity.Order;\n\n\npublic interface OrderDao {\n\n    public int insert(Order order);\n\n    public int update(Order order);\n\n    Order findByMerchantOrderNo(String merchantOrderNo);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/OrderLineDao.java",
    "content": "package io.tyloo.sample.order.infrastructure.dao;\n\n\nimport io.tyloo.sample.order.domain.entity.OrderLine;\n\n\npublic interface OrderLineDao {\n    void insert(OrderLine orderLine);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/ProductDao.java",
    "content": "package io.tyloo.sample.order.infrastructure.dao;\n\n\nimport io.tyloo.sample.order.domain.entity.Product;\n\nimport java.util.List;\n\n\npublic interface ProductDao {\n\n    Product findById(long productId);\n\n    List<Product> findByShopId(long shopId);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/ShopDao.java",
    "content": "package io.tyloo.sample.order.infrastructure.dao;\n\n\nimport io.tyloo.sample.order.domain.entity.Shop;\n\n\npublic interface ShopDao {\n    Shop findById(long id);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/spring/local/appcontext-service-dao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:jdbc=\"http://www.springframework.org/schema/jdbc\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample\"/>\n\n    <bean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\">\n        <property name=\"dataSource\" ref=\"orderDataSource\"/>\n        <property name=\"mapperLocations\" value=\"classpath*:config/sqlmap/main/sample-*.xml\"/>\n    </bean>\n\n    <bean id=\"orderDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.order.infrastructure.dao.OrderDao\"></property>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"></property>\n    </bean>\n\n    <bean id=\"orderLineDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.order.infrastructure.dao.OrderLineDao\"></property>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"></property>\n    </bean>\n\n    <bean id=\"shopDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.order.infrastructure.dao.ShopDao\"></property>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"></property>\n    </bean>\n\n    <bean id=\"productDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.order.infrastructure.dao.ProductDao\"/>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/>\n    </bean>\n\n    <jdbc:embedded-database id=\"orderDataSource\"/>\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/spring/local/appcontext-service-datasource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd\n         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <util:properties id=\"jdbc\" location=\"classpath:jdbc.properties\"/>\n\n    <bean id=\"orderDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{jdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{jdbc['jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{jdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{jdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{jdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{jdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{jdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{jdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{jdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{jdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <bean id=\"transactionManager\"\n          class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n        <property name=\"dataSource\" ref=\"orderDataSource\"/>\n    </bean>\n\n    <tx:annotation-driven transaction-manager=\"transactionManager\"/>\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-order.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.order.infrastructure.dao.OrderDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.order.domain.entity.Order\">\n        <id property=\"id\" javaType=\"java.lang.Long\" column=\"ORDER_ID\"/>\n        <result property=\"payerUserId\" column=\"PAYER_USER_ID\"/>\n        <result property=\"payeeUserId\" column=\"PAYEE_USER_ID\"/>\n        <result property=\"redPacketPayAmount\" column=\"RED_PACKET_PAY_AMOUNT\"/>\n        <result property=\"capitalPayAmount\" column=\"CAPITAL_PAY_AMOUNT\"/>\n        <result property=\"status\" column=\"STATUS\"/>\n        <result property=\"merchantOrderNo\" column=\"MERCHANT_ORDER_NO\"/>\n        <result property=\"version\" column=\"VERSION\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        ORDER_ID,\n        PAYER_USER_ID,\n        PAYEE_USER_ID,\n        RED_PACKET_PAY_AMOUNT,\n        CAPITAL_PAY_AMOUNT,\n        STATUS,\n        MERCHANT_ORDER_NO,\n        VERSION\n        FROM\n        ORD_ORDER\n    </sql>\n\n\n    <select id=\"findAll\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n    </select>\n\n    <select id=\"findById\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE ORDER_ID = #{id,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n\n    <select id=\"findByMerchantOrderNo\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE MERCHANT_ORDER_NO = #{findByMerchantOrderNo}\n    </select>\n\n    <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO ORD_ORDER\n        (\n        PAYER_USER_ID,\n        PAYEE_USER_ID,\n        RED_PACKET_PAY_AMOUNT,\n        CAPITAL_PAY_AMOUNT,\n        STATUS,\n        MERCHANT_ORDER_NO,\n        VERSION\n        )\n        VALUES\n            (\n            #{payerUserId},\n            #{payeeUserId},\n            #{redPacketPayAmount},\n            #{capitalPayAmount},\n            #{status},\n            #{merchantOrderNo},\n            #{version}\n            )\n    </insert>\n\n    <update id=\"update\" parameterType=\"io.tyloo.sample.order.domain.entity.Order\">\n        UPDATE\n        ORD_ORDER\n        SET\n        STATUS = #{status},\n        RED_PACKET_PAY_AMOUNT = #{redPacketPayAmount},\n        CAPITAL_PAY_AMOUNT = #{capitalPayAmount},\n        VERSION = #{version}\n        WHERE ORDER_ID = #{id} AND VERSION=#{version}-1\n    </update>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-orderline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.order.infrastructure.dao.OrderLineDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.order.domain.entity.OrderLine\">\n        <result property=\"id\" javaType=\"java.lang.Long\" column=\"ORDER_LINE_ID\"/>\n        <result property=\"productId\" column=\"PRODUCT_ID\"/>\n        <result property=\"quantity\" column=\"QUANTITY\"/>\n        <result property=\"unitPrice\" column=\"UNIT_PRICE\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        ORDER_LINE_ID,\n        PRODUCT_ID,\n        QUANTITY,\n        UNIT_PRICE\n        FROM\n        TCC_ORDER_LINE\n    </sql>\n\n    <select id=\"findById\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE ORDER_LINE_ID = #{id,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n\n    <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO ORD_ORDER_LINE\n        (\n        PRODUCT_ID,\n        QUANTITY,\n        UNIT_PRICE\n        )\n        VALUES\n        (\n        #{productId},\n        #{quantity},\n        #{unitPrice}\n        )\n    </insert>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-product.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.order.infrastructure.dao.ProductDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.order.domain.entity.Product\">\n        <id property=\"productId\" javaType=\"java.lang.Long\" column=\"PRODUCT_ID\"/>\n        <result property=\"shopId\" column=\"SHOP_ID\"/>\n        <result property=\"productName\" column=\"PRODUCT_NAME\"/>\n        <result property=\"price\" column=\"PRICE\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        PRODUCT_ID,\n        SHOP_ID,\n        PRODUCT_NAME,\n        PRICE\n        FROM\n        ORD_PRODUCT\n    </sql>\n\n    <select id=\"findById\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE PRODUCT_ID = #{productId,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n\n    <select id=\"findByShopId\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE SHOP_ID = #{shopId,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n</mapper>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-shop.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.order.infrastructure.dao.ShopDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.order.domain.entity.Shop\">\n        <id property=\"id\" javaType=\"java.lang.Long\" column=\"SHOP_ID\"/>\n        <result property=\"ownerUserId\" column=\"OWNER_USER_ID\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        SHOP_ID,\n        OWNER_USER_ID\n        FROM\n        ORD_SHOP\n    </sql>\n\n    <select id=\"findById\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE SHOP_ID = #{id,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/jdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://localhost:3306/TCC_ORD?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=3000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-order]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/sample-order.properties",
    "content": "\n\nzookeeper.address=127.0.0.1:2181\nzookeeper.session.timeout=1800000\nzookeeper.connect.timeout=30000"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>tyloo-sample-domain</artifactId>\n        <groupId>io.tyloo</groupId>\n        <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-sample-redpacket</artifactId>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-sample-common</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>log4j</groupId>\n            <artifactId>log4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.mchange</groupId>\n            <artifactId>c3p0</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis-spring</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis</groupId>\n            <artifactId>mybatis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/entity/RedPacketAccount.java",
    "content": "package io.tyloo.sample.redpacket.domain.entity;\n\nimport io.tyloo.sample.exception.InsufficientBalanceException;\n\nimport java.math.BigDecimal;\n\n\npublic class RedPacketAccount {\n\n    private long id;\n\n    private long userId;\n\n    private BigDecimal balanceAmount;\n\n    public long getUserId() {\n        return userId;\n    }\n\n    public BigDecimal getBalanceAmount() {\n        return balanceAmount;\n    }\n\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public void transferFrom(BigDecimal amount) {\n        this.balanceAmount = this.balanceAmount.subtract(amount);\n\n        if (BigDecimal.ZERO.compareTo(this.balanceAmount) > 0) {\n            throw new InsufficientBalanceException();\n        }\n    }\n\n    public void transferTo(BigDecimal amount) {\n        this.balanceAmount = this.balanceAmount.add(amount);\n    }\n\n    public void cancelTransfer(BigDecimal amount) {\n        transferTo(amount);\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/entity/TradeOrder.java",
    "content": "package io.tyloo.sample.redpacket.domain.entity;\n\nimport java.math.BigDecimal;\n\npublic class TradeOrder {\n\n    private long id;\n\n    private long selfUserId;\n\n    private long oppositeUserId;\n\n    private String merchantOrderNo;\n\n    private BigDecimal amount;\n\n    private String status = \"DRAFT\";\n\n    private long version = 1l;\n\n    public TradeOrder() {\n    }\n\n    public TradeOrder(long selfUserId, long oppositeUserId, String merchantOrderNo, BigDecimal amount) {\n        this.selfUserId = selfUserId;\n        this.oppositeUserId = oppositeUserId;\n        this.merchantOrderNo = merchantOrderNo;\n        this.amount = amount;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public long getSelfUserId() {\n        return selfUserId;\n    }\n\n    public long getOppositeUserId() {\n        return oppositeUserId;\n    }\n\n    public String getMerchantOrderNo() {\n        return merchantOrderNo;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void confirm() {\n        this.status = \"CONFIRM\";\n    }\n\n    public void cancel() {\n        this.status = \"CANCEL\";\n    }\n\n    public long getVersion() {\n        return version;\n    }\n\n    public void updateVersion() {\n        version = version + 1;\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/repository/RedPacketAccountRepository.java",
    "content": "package io.tyloo.sample.redpacket.domain.repository;\n\n\nimport io.tyloo.sample.exception.InsufficientBalanceException;\nimport io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;\nimport io.tyloo.sample.redpacket.infrastructure.dao.RedPacketAccountDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Repository;\n\n\n@Repository\npublic class RedPacketAccountRepository {\n\n    @Autowired\n    RedPacketAccountDao redPacketAccountDao;\n\n    public RedPacketAccount findByUserId(long userId) {\n\n        return redPacketAccountDao.findByUserId(userId);\n    }\n\n    public void save(RedPacketAccount redPacketAccount) {\n        int effectCount = redPacketAccountDao.update(redPacketAccount);\n        if (effectCount < 1) {\n            throw new InsufficientBalanceException();\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/repository/TradeOrderRepository.java",
    "content": "package io.tyloo.sample.redpacket.domain.repository;\n\nimport io.tyloo.sample.redpacket.domain.entity.TradeOrder;\nimport io.tyloo.sample.redpacket.infrastructure.dao.TradeOrderDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.OptimisticLockingFailureException;\nimport org.springframework.stereotype.Repository;\n\n\n@Repository\npublic class TradeOrderRepository {\n\n    @Autowired\n    TradeOrderDao tradeOrderDao;\n\n    public void insert(TradeOrder tradeOrder) {\n        tradeOrderDao.insert(tradeOrder);\n    }\n\n    public void update(TradeOrder tradeOrder) {\n\n        tradeOrder.updateVersion();\n        int effectCount = tradeOrderDao.update(tradeOrder);\n        if (effectCount < 1) {\n            throw new OptimisticLockingFailureException(\"update trade order failed\");\n        }\n    }\n\n    public TradeOrder findByMerchantOrderNo(String merchantOrderNo) {\n        return tradeOrderDao.findByMerchantOrderNo(merchantOrderNo);\n    }\n\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/infrastructure/dao/RedPacketAccountDao.java",
    "content": "package io.tyloo.sample.redpacket.infrastructure.dao;\n\nimport io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;\n\n\npublic interface RedPacketAccountDao {\n\n    RedPacketAccount findByUserId(long userId);\n\n    int update(RedPacketAccount redPacketAccount);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/infrastructure/dao/TradeOrderDao.java",
    "content": "package io.tyloo.sample.redpacket.infrastructure.dao;\n\n\nimport io.tyloo.sample.redpacket.domain.entity.TradeOrder;\n\n\npublic interface TradeOrderDao {\n\n    void insert(TradeOrder tradeOrder);\n\n    int update(TradeOrder tradeOrder);\n\n    TradeOrder findByMerchantOrderNo(String merchantOrderNo);\n}\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/spring/local/appcontext-service-dao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:jdbc=\"http://www.springframework.org/schema/jdbc\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.sample\"/>\n\n    <bean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\">\n        <property name=\"dataSource\" ref=\"redPacketDataSource\"/>\n        <property name=\"mapperLocations\" value=\"classpath*:config/sqlmap/main/sample-*.xml\"/>\n    </bean>\n\n    <bean id=\"redPacketAccountDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.redpacket.infrastructure.dao.RedPacketAccountDao\"></property>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"></property>\n    </bean>\n\n    <bean id=\"tradeOrderDao\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">\n        <property name=\"mapperInterface\"\n                  value=\"io.tyloo.sample.redpacket.infrastructure.dao.TradeOrderDao\"/>\n        <property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/>\n    </bean>\n\n    <jdbc:embedded-database id=\"redPacketDataSource\"/>\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/spring/local/appcontext-service-datasource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd\n         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">\n\n    <util:properties id=\"jdbc\" location=\"classpath:jdbc.properties\"/>\n\n    <bean id=\"redPacketDataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n          destroy-method=\"close\" lazy-init=\"false\">\n        <property name=\"driverClass\" value=\"#{jdbc['jdbc.driverClassName']}\"/>\n        <property name=\"jdbcUrl\" value=\"#{jdbc['jdbc.url']}\"/>\n        <property name=\"user\" value=\"#{jdbc['jdbc.username']}\"/>\n        <property name=\"password\" value=\"#{jdbc['jdbc.password']}\"/>\n        <property name=\"initialPoolSize\" value=\"#{jdbc['c3p0.initialPoolSize']}\"/>\n        <property name=\"minPoolSize\" value=\"#{jdbc['c3p0.minPoolSize']}\"/>\n        <property name=\"maxPoolSize\" value=\"#{jdbc['c3p0.maxPoolSize']}\"/>\n        <property name=\"acquireIncrement\" value=\"#{jdbc['c3p0.acquireIncrement']}\"/>\n        <property name=\"maxIdleTime\" value=\"#{jdbc['c3p0.maxIdleTime']}\"/>\n        <property name=\"checkoutTimeout\" value=\"#{jdbc['c3p0.checkoutTimeout']}\"/>\n    </bean>\n\n    <bean id=\"transactionManager\"\n          class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n        <property name=\"dataSource\" ref=\"redPacketDataSource\"/>\n    </bean>\n\n    <tx:annotation-driven transaction-manager=\"transactionManager\"/>\n\n\n</beans>"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/sqlmap/main/sample-redpacketaccount.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.redpacket.infrastructure.dao.RedPacketAccountDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.redpacket.domain.entity.RedPacketAccount\">\n        <id property=\"id\" javaType=\"java.lang.Long\" column=\"RED_PACKET_ACCOUNT_ID\"/>\n        <result property=\"balanceAmount\" column=\"BALANCE_AMOUNT\"/>\n        <result property=\"userId\" column=\"USER_ID\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        RED_PACKET_ACCOUNT_ID,\n        BALANCE_AMOUNT,\n        USER_ID\n        FROM\n        RED_RED_PACKET_ACCOUNT\n    </sql>\n\n\n    <select id=\"findAll\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n    </select>\n\n    <select id=\"findByUserId\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE USER_ID = #{id,javaType=java.lang.Long,jdbcType=BIGINT}\n    </select>\n\n    <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO RED_RED_PACKET_ACCOUNT\n        (\n        BALANCE_AMOUNT,\n        USER_ID\n        )\n        VALUES\n            (\n            #{balanceAmount},\n            #{userId}\n            )\n    </insert>\n\n    <update id=\"update\" parameterType=\"io.tyloo.sample.redpacket.domain.entity.RedPacketAccount\">\n        UPDATE\n        RED_RED_PACKET_ACCOUNT\n        SET\n        BALANCE_AMOUNT = #{balanceAmount}\n        WHERE RED_PACKET_ACCOUNT_ID = #{id}\n    </update>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/sqlmap/main/sample-tradeorder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"io.tyloo.sample.redpacket.infrastructure.dao.TradeOrderDao\">\n\n    <resultMap id=\"result\"\n               type=\"io.tyloo.sample.redpacket.domain.entity.TradeOrder\">\n        <id property=\"id\" javaType=\"java.lang.Long\" column=\"ID\"/>\n        <result property=\"selfUserId\" column=\"SELF_USER_ID\"/>\n        <result property=\"oppositeUserId\" column=\"OPPOSITE_USER_ID\"/>\n        <result property=\"merchantOrderNo\" column=\"MERCHANT_ORDER_NO\"/>\n        <result property=\"amount\" column=\"AMOUNT\"/>\n        <result property=\"status\" column=\"STATUS\"/>\n        <result property=\"version\" column=\"VERSION\"/>\n    </resultMap>\n\n    <sql id=\"sql_select\">\n        SELECT\n        ID,\n        SELF_USER_ID,\n        OPPOSITE_USER_ID,\n        MERCHANT_ORDER_NO,\n        AMOUNT,\n        STATUS,\n        VERSION\n        FROM\n        RED_TRADE_ORDER\n    </sql>\n\n    <select id=\"findByMerchantOrderNo\" resultMap=\"result\">\n        <include refid=\"sql_select\"/>\n        WHERE MERCHANT_ORDER_NO = #{merchantOrderNo}\n    </select>\n\n    <insert id=\"insert\" useGeneratedKeys=\"true\" keyProperty=\"id\"\n            parameterType=\"io.tyloo.sample.redpacket.domain.entity.TradeOrder\">\n        INSERT INTO RED_TRADE_ORDER\n        (\n        SELF_USER_ID,\n        OPPOSITE_USER_ID,\n        MERCHANT_ORDER_NO,\n        AMOUNT,\n        STATUS,\n        VERSION\n        )\n        VALUES\n        (\n        #{selfUserId},\n        #{oppositeUserId},\n        #{merchantOrderNo},\n        #{amount},\n        #{status},\n        #{version}\n        )\n    </insert>\n\n    <update id=\"update\" parameterType=\"io.tyloo.sample.redpacket.domain.entity.TradeOrder\">\n        UPDATE\n        RED_TRADE_ORDER\n        SET\n        STATUS = #{status},\n        VERSION = #{version}\n        WHERE ID = #{id} AND VERSION = #{version}-1\n    </update>\n</mapper>\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/jdbc.properties",
    "content": "jdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_RED?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true\njdbc.username=root\njdbc.password=root\n\nc3p0.initialPoolSize=10\nc3p0.minPoolSize=10\nc3p0.maxPoolSize=30\nc3p0.acquireIncrement=3\nc3p0.maxIdleTime=1800\nc3p0.checkoutTimeout=30000\n"
  },
  {
    "path": "tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/log/log4j.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE log4j:configuration PUBLIC \"-//log4j/log4j Configuration//EN\" \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\"\n                     threshold=\"null\" debug=\"null\">\n\n    <appender name=\"CONSOLE\" class=\"org.apache.log4j.ConsoleAppender\">\n        <param name=\"Target\" value=\"System.out\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"[sample-dubbo-redpacket]%d %-5p [%c] %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"asyncAppender\" class=\"org.apache.log4j.AsyncAppender\">\n        <param name=\"BufferSize\" value=\"4096\" />\n        <param name=\"Blocking\" value=\"false\"/>\n        <appender-ref ref=\"CONSOLE\" />\n    </appender>\n\n    <root>\n        <level value=\"INFO\"/>\n        <appender-ref ref=\"asyncAppender\"/>\n    </root>\n\n</log4j:configuration>\n"
  },
  {
    "path": "tyloo-unit-test/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>io.tyloo</groupId>\n        <artifactId>tyloo</artifactId>\n        <version>1.1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>tyloo-unit-test</artifactId>\n    <packaging>jar</packaging>\n    <dependencies>\n\n        <dependency>\n            <groupId>io.tyloo</groupId>\n            <artifactId>tyloo-spring</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-test</artifactId>\n        </dependency>\n\n        <!--<dependency>-->\n        <!--<groupId>org.springframework</groupId>-->\n        <!--<artifactId>spring-tx</artifactId>-->\n        <!--</dependency>-->\n        <!--<dependency>-->\n        <!--<groupId>org.springframework</groupId>-->\n        <!--<artifactId>spring-jdbc</artifactId>-->\n        <!--</dependency>-->\n        <!--<dependency>-->\n        <!--<groupId>org.springframework</groupId>-->\n        <!--<artifactId>spring-aop</artifactId>-->\n        <!--</dependency>-->\n        <!--<dependency>-->\n        <!--<groupId>org.springframework</groupId>-->\n        <!--<artifactId>spring-test</artifactId>-->\n        <!--</dependency>-->\n        <!--<dependency>-->\n        <!--<groupId>aopalliance</groupId>-->\n        <!--<artifactId>aopalliance</artifactId>-->\n        <!--</dependency>-->\n        <!--<dependency>-->\n        <!--<groupId>cglib</groupId>-->\n        <!--<artifactId>cglib</artifactId>-->\n        <!--</dependency>-->\n\n        <!--<dependency>-->\n        <!--<groupId>org.aspectj</groupId>-->\n        <!--<artifactId>aspectjweaver</artifactId>-->\n        <!--</dependency>-->\n    </dependencies>\n</project>"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/client/AccountRecordServiceProxy.java",
    "content": "package io.tyloo.unittest.client;\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.unittest.thirdservice.AccountRecordService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\n\nimport static java.util.concurrent.Executors.*;\n\n\n@Service\npublic class AccountRecordServiceProxy {\n\n\n    @Autowired\n    private AccountRecordService accountRecordService;\n\n    private ExecutorService executorService = newFixedThreadPool(100);\n\n    public void record(final TransactionContext transactionContext, final long accountId, final int amount) {\n//        Future<Boolean> future = this.executorService\n//                .submit(new Callable<Boolean>() {\n//                    @Override\n//                    public Boolean call() throws Exception {\n//                        accountRecordService.record(transactionContext, accountId, amount);\n//                        return true;\n//                    }\n//                });\n//\n//        handleResult(future);\n\n        accountRecordService.record(transactionContext, accountId, amount);\n\n    }\n\n    private void handleResult(Future<Boolean> future) {\n        while (!future.isDone()) {\n            try {\n                Thread.sleep(300);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n        try {\n            future.get();\n        } catch (InterruptedException | ExecutionException e) {\n            throw new Error(e);\n        }\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/client/AccountServiceProxy.java",
    "content": "package io.tyloo.unittest.client;\n\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.unittest.service.AccountService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.*;\n\n\n@Service\npublic class AccountServiceProxy {\n\n    @Autowired\n    AccountService accountService;\n\n    private ExecutorService executorService = Executors.newFixedThreadPool(100);\n\n    public void transferFromWithMultipleTier(final TransactionContext transactionContext, final long accountId, final int amount) {\n        Future<Boolean> future = this.executorService\n                .submit(new Callable<Boolean>() {\n                    @Override\n                    public Boolean call() throws Exception {\n                        accountService.transferFromWithMultipleTier(transactionContext, accountId, amount);\n                        return true;\n                    }\n                });\n\n        handleResult(future);\n    }\n\n    public void transferToWithMultipleTier(final TransactionContext transactionContext, final long accountId, final int amount) {\n//        Future<Boolean> future = this.executorService\n//                .submit(new Callable<Boolean>() {\n//                    @Override\n//                    public Boolean call() throws Exception {\n//                        accountService.transferToWithMultipleTier(transactionContext, accountId, amount);\n//                        return true;\n//                    }\n//                });\n//\n//        handleResult(future);\n        accountService.transferToWithMultipleTier(transactionContext, accountId, amount);\n    }\n\n    public void performanceTuningTransferTo(TransactionContext transactionContext) {\n    }\n\n    public void transferTo(final TransactionContext transactionContext, final long accountId, final int amount) {\n\n//        Future<Boolean> future = this.executorService\n//                .submit(new Callable<Boolean>() {\n//                    @Override\n//                    public Boolean call() throws Exception {\n//                        accountService.transferTo(transactionContext, accountId, amount);\n//                        return true;\n//                    }\n//                });\n//\n//        handleResult(future);\n        accountService.transferTo(transactionContext, accountId, amount);\n    }\n\n    public void transferTo(final long accountId, final int amount) {\n\n//        Future<Boolean> future = this.executorService\n//                .submit(new Callable<Boolean>() {\n//                    @Override\n//                    public Boolean call() throws Exception {\n//                        accountService.transferToWithNoTransactionContext(accountId, amount);\n//                        return true;\n//                    }\n//                });\n//\n//        handleResult(future);\n\n        accountService.transferToWithNoTransactionContext(accountId, amount);\n    }\n\n    public void transferFrom(final TransactionContext transactionContext, final long accountId, final int amount) {\n\n//        Future<Boolean> future = this.executorService\n//                .submit(new Callable<Boolean>() {\n//                    @Override\n//                    public Boolean call() throws Exception {\n//                        accountService.transferFrom(transactionContext, accountId, amount);\n//                        return true;\n//                    }\n//                });\n//\n//        handleResult(future);\n\n        accountService.transferFrom(transactionContext, accountId, amount);\n    }\n\n\n    private void handleResult(Future<Boolean> future) {\n        while (!future.isDone()) {\n            try {\n                Thread.sleep(300);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n        try {\n            future.get();\n        } catch (InterruptedException e) {\n            throw new Error(e);\n        } catch (ExecutionException e) {\n            throw new Error(e);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/client/TransferService.java",
    "content": "package io.tyloo.unittest.client;\n\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.Propagation;\nimport io.tyloo.unittest.entity.AccountStatus;\nimport io.tyloo.unittest.entity.SubAccount;\nimport io.tyloo.unittest.repository.SubAccountRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n\n@Service\npublic class TransferService {\n\n    @Autowired\n    AccountServiceProxy accountService;\n\n    @Autowired\n    SubAccountRepository subAccountRepository;\n\n    public TransferService() {\n    }\n\n\n    @Tyloo\n    @Transactional\n    public void performenceTuningTransfer() {\n        accountService.performanceTuningTransferTo(null);\n    }\n\n    @Tyloo(propagation = Propagation.MANDATORY)\n    public void transferWithMandatoryPropagation(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transfer called\");\n\n        SubAccount subAccount = subAccountRepository.findById(fromAccountId);\n\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);\n\n        accountService.transferTo(null, toAccountId, amount);\n    }\n\n    @Tyloo(confirmMethod = \"transferConfirm\", cancelMethod = \"transferCancel\")\n    @Transactional\n    public void transfer(long fromAccountId, long toAccountId, int amount) {\n\n        System.out.println(\"transfer called\");\n\n        SubAccount subAccount = subAccountRepository.findById(fromAccountId);\n\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);\n\n        accountService.transferTo(null, toAccountId, amount);\n    }\n\n    @Tyloo(confirmMethod = \"transferConfirm\", cancelMethod = \"transferCancel\")\n    public void transferWithMultipleTier(long fromAccountId, long toAccountId, int amount) {\n\n        System.out.println(\"transferWithMultipleTier called\");\n\n        SubAccount subAccount = subAccountRepository.findById(fromAccountId);\n\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);\n\n        accountService.transferToWithMultipleTier(null, toAccountId, amount);\n    }\n\n    @Tyloo(confirmMethod = \"transferWithMultipleConsumerConfirm\", cancelMethod = \"transferWithMultipleConsumerCancel\")\n    @Transactional\n    public void transferWithMultipleConsumer(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transferWithMultipleConsumer called\");\n        accountService.transferFrom(null, fromAccountId, amount);\n        accountService.transferTo(null, toAccountId, amount);\n    }\n\n    @Tyloo\n    public void transferWithOnlyTryAndMultipleConsumer(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transferWithOnlyTryAndMultipleConsumer called\");\n        accountService.transferFrom(null, fromAccountId, amount);\n        accountService.transferTo(null, toAccountId, amount);\n    }\n\n    @Tyloo\n    public void transferWithNoTransactionContext(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transferWithNoTransactionContext called\");\n        accountService.transferTo(toAccountId, amount);\n    }\n\n    public void transferConfirm(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transferConfirm called\");\n        SubAccount subAccount = subAccountRepository.findById(fromAccountId);\n        subAccount.setStatus(AccountStatus.NORMAL.getId());\n    }\n\n    public void transferCancel(long fromAccountId, long toAccountId, int amount) {\n\n        System.out.println(\"transferCancel called\");\n\n        SubAccount subAccount = subAccountRepository.findById(fromAccountId);\n\n        if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {\n            subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);\n        }\n\n        subAccount.setStatus(AccountStatus.NORMAL.getId());\n    }\n\n    public void transferWithMultipleConsumerConfirm(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transferWithMultipleConsumerConfirm called\");\n    }\n\n    public void transferWithMultipleConsumerCancel(long fromAccountId, long toAccountId, int amount) {\n        System.out.println(\"transferWithMultipleConsumerCancel called\");\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/AccountRecord.java",
    "content": "package io.tyloo.unittest.entity;\n\n\npublic class AccountRecord {\n\n    private long accountId;\n\n    private  volatile int balanceAmount;\n\n    private volatile int statusId = AccountStatus.NORMAL.getId();\n\n    public AccountRecord(long accountId, int balanceAmount) {\n        this.accountId = accountId;\n        this.balanceAmount = balanceAmount;\n    }\n\n    public long getAccountId() {\n        return accountId;\n    }\n\n    public int getBalanceAmount() {\n        return balanceAmount;\n    }\n\n    public void setStatusId(int statusId) {\n        this.statusId = statusId;\n    }\n\n    public void setBalanceAmount(int balanceAmount) {\n        this.balanceAmount = balanceAmount;\n    }\n\n    public int getStatusId() {\n        return statusId;\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/AccountStatus.java",
    "content": "package io.tyloo.unittest.entity;\n\n\npublic enum AccountStatus {\n\n    NORMAL(1),\n\n    TRANSFERING(2);\n\n    private int id;\n\n    AccountStatus(int id) {\n        this.id = id;\n    }\n\n    public int getId() {\n        return id;\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/SubAccount.java",
    "content": "package io.tyloo.unittest.entity;\n\n\npublic class SubAccount {\n\n    private long id;\n\n    private volatile int balanceAmount;\n\n    private volatile int status = AccountStatus.NORMAL.getId();\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public int getBalanceAmount() {\n        return balanceAmount;\n    }\n\n    public void setBalanceAmount(int balanceAmount) {\n        this.balanceAmount = balanceAmount;\n    }\n\n    public SubAccount() {\n\n    }\n\n    public SubAccount(long id, int balanceAmount) {\n        this.id = id;\n        this.balanceAmount = balanceAmount;\n    }\n\n    public void setStatus(int status) {\n        this.status = status;\n    }\n\n    public int getStatus() {\n        return status;\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/UserShardingId.java",
    "content": "package io.tyloo.unittest.entity;\n\n\nimport java.io.Serializable;\n\n\npublic abstract class UserShardingId implements Serializable {\n\n    private static final long serialVersionUID = -8923642703284688507L;\n    private Long id;\n\n    private Long userId;\n\n    public UserShardingId() {\n    }\n\n    public UserShardingId(Long userId) {\n        this.userId = userId;\n    }\n\n    public UserShardingId(Long id, Long userId) {\n\n        this.id = id;\n        this.userId = userId;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public void setUserId(Long userId) {\n        this.userId = userId;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n\n        if (null == obj) {\n            return false;\n        }\n\n        if (this == obj) {\n            return true;\n        }\n\n        if (!getClass().equals(obj.getClass())) {\n            return false;\n        }\n\n        UserShardingId that = (UserShardingId) obj;\n\n        boolean isIdEquals = false;\n\n        boolean isUserIdEquals = false;\n\n        if ((this.id == null && that.id == null) || (this.id != null && this.id.equals(that.id))) {\n            isIdEquals = true;\n        }\n\n        if ((this.userId == null && that.userId == null) || (this.userId != null && this.userId.equals(that.userId))) {\n            isUserIdEquals = true;\n        }\n\n        return isIdEquals && isUserIdEquals;\n    }\n\n    @Override\n    public int hashCode() {\n\n        int hashCode = 17;\n\n        hashCode += this.id == null ? 0 : this.id * 31;\n\n        hashCode += this.userId == null ? 0 : this.userId * 31;\n\n        return hashCode;\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/repository/AccountRecordRepository.java",
    "content": "package io.tyloo.unittest.repository;\n\nimport io.tyloo.unittest.entity.AccountRecord;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n\n@Repository\npublic class AccountRecordRepository {\n\n    private Map<Long, AccountRecord> accountRecordMap = new HashMap<Long, AccountRecord>();\n\n    {\n        accountRecordMap.put(1L, new AccountRecord(1, 0));\n        accountRecordMap.put(2L, new AccountRecord(2, 0));\n        accountRecordMap.put(3L, new AccountRecord(3, 0));\n    }\n\n    public AccountRecord findById(Long id) {\n        return accountRecordMap.get(id);\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/repository/SubAccountRepository.java",
    "content": "package io.tyloo.unittest.repository;\n\nimport io.tyloo.unittest.entity.SubAccount;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n\n@Repository\npublic class SubAccountRepository {\n\n    private Map<Long, SubAccount> subAccountMap = new HashMap<Long, SubAccount>();\n\n    {\n        subAccountMap.put(1L, new SubAccount(1, 100));\n        subAccountMap.put(2L, new SubAccount(2, 200));\n        subAccountMap.put(3L, new SubAccount(3, 300));\n    }\n\n    public SubAccount findById(Long id) {\n        return subAccountMap.get(id);\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/service/AccountService.java",
    "content": "package io.tyloo.unittest.service;\n\nimport io.tyloo.api.TransactionContext;\n\n\npublic interface AccountService {\n\n    void transferTo(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferToConfirm(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferToCancel(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferToWithNoTransactionContext(long accountId, int amount);\n\n    void transferToConfirmWithNoTransactionContext(long accountId, int amount);\n\n    void transferToCancelWithNoTransactionContext(long accountId, int amount);\n\n    void transferFrom(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferFromConfirm(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferFromCancel(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferToWithMultipleTier(TransactionContext transactionContext, long accountId, int amount);\n\n    void transferFromWithMultipleTier(TransactionContext transactionContext, long accountId, int amount);\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/service/AccountServiceImpl.java",
    "content": "package io.tyloo.unittest.service;\n\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.Propagation;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.unittest.client.AccountRecordServiceProxy;\nimport io.tyloo.unittest.entity.AccountStatus;\nimport io.tyloo.unittest.entity.SubAccount;\nimport io.tyloo.unittest.repository.SubAccountRepository;\nimport io.tyloo.unittest.utils.UnitTest;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n\n@Service\npublic class AccountServiceImpl implements AccountService {\n\n\n    @Autowired\n    AccountRecordServiceProxy accountRecordServiceProxy;\n\n    @Autowired\n    SubAccountRepository subAccountRepository;\n\n    @Override\n    @Tyloo(confirmMethod = \"transferFromConfirm\", cancelMethod = \"transferFromCancel\")\n    public void transferFrom(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"transferFrom called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);\n        accountRecordServiceProxy.record(null, accountId, amount);\n    }\n\n    @Override\n    @Tyloo(propagation = Propagation.REQUIRED, confirmMethod = \"transferToConfirm\", cancelMethod = \"transferToCancel\")\n    public void transferTo(TransactionContext transactionContext, long accountId, int amount) {\n\n        System.out.println(\"transferTo called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);\n    }\n\n    @Override\n    @Tyloo(confirmMethod = \"transferFromConfirm\", cancelMethod = \"transferFromCancel\")\n    public void transferFromWithMultipleTier(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"transferFromWithMultipleTier called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);\n        accountRecordServiceProxy.record(null, accountId, amount);\n    }\n\n    @Override\n    @Tyloo(confirmMethod = \"transferToConfirm\", cancelMethod = \"transferToCancel\")\n    public void transferToWithMultipleTier(TransactionContext transactionContext, long accountId, int amount) {\n\n        System.out.println(\"transferToWithMultipleTier called\");\n\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);\n\n        accountRecordServiceProxy.record(null, accountId, amount);\n    }\n\n\n    @Override\n    @Tyloo(propagation = Propagation.REQUIRES_NEW, confirmMethod = \"transferToConfirmWithNoTransactionContext\", cancelMethod = \"transferToCancelWithNoTransactionContext\")\n    public void transferToWithNoTransactionContext(long accountId, int amount) {\n\n        System.out.println(\"transferToWithNoTransactionContext called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.TRANSFERING.getId());\n        subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);\n        accountRecordServiceProxy.record(null, accountId, amount);\n    }\n\n\n    public void transferFromConfirm(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"transferFromConfirm called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.NORMAL.getId());\n    }\n\n    public void transferFromCancel(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"transferFromCancel called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n\n        if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {\n\n            subAccount.setStatus(AccountStatus.NORMAL.getId());\n            subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);\n        }\n    }\n\n    @Override\n    public void transferToConfirm(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"transferToConfirm called\");\n\n        if (UnitTest.CONFIRMING_EXCEPTION) {\n            throw new RuntimeException(\"transferToConfirm confirm failed.\");\n        }\n\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.NORMAL.getId());\n    }\n\n    @Override\n    public void transferToCancel(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"transferToCancel called\");\n\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n\n        if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {\n\n            subAccount.setStatus(AccountStatus.NORMAL.getId());\n            subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);\n        }\n    }\n\n    public void transferToConfirmWithNoTransactionContext(long accountId, int amount) {\n        System.out.println(\"transferToConfirmWithNoTransactionContext called\");\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n        subAccount.setStatus(AccountStatus.NORMAL.getId());\n    }\n\n    public void transferToCancelWithNoTransactionContext(long accountId, int amount) {\n        System.out.println(\"transferToCancelWithNoTransactionContext called\");\n\n        SubAccount subAccount = subAccountRepository.findById(accountId);\n\n        if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {\n\n            subAccount.setStatus(AccountStatus.NORMAL.getId());\n            subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);\n        }\n    }\n\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/thirdservice/AccountRecordService.java",
    "content": "package io.tyloo.unittest.thirdservice;\n\nimport io.tyloo.api.TransactionContext;\n\n\npublic interface AccountRecordService {\n    public void record(TransactionContext transactionContext, long accountId, int amount);\n\n    void recordConfirm(TransactionContext transactionContext, long accountId, int amount);\n\n    void recordCancel(TransactionContext transactionContext, long accountId, int amount);\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/thirdservice/AccountRecordServiceImpl.java",
    "content": "package io.tyloo.unittest.thirdservice;\n\nimport io.tyloo.api.Tyloo;\nimport io.tyloo.api.TransactionContext;\nimport io.tyloo.unittest.entity.AccountRecord;\nimport io.tyloo.unittest.entity.AccountStatus;\nimport io.tyloo.unittest.repository.AccountRecordRepository;\nimport io.tyloo.unittest.utils.UnitTest;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n\n@Service\npublic class AccountRecordServiceImpl implements AccountRecordService {\n\n    @Autowired\n    AccountRecordRepository accountRecordRepository;\n\n    @Override\n    @Tyloo(confirmMethod = \"recordConfirm\", cancelMethod = \"recordCancel\")\n    public void record(TransactionContext transactionContext, long accountId, int amount) {\n\n        System.out.println(\"record\");\n\n        AccountRecord accountRecord = accountRecordRepository.findById(accountId);\n        accountRecord.setBalanceAmount(amount);\n        accountRecord.setStatusId(AccountStatus.TRANSFERING.getId());\n\n        if (UnitTest.TRYING_EXCEPTION) {\n            throw new RuntimeException(\"record try failed.\");\n        }\n    }\n\n    @Override\n    public void recordConfirm(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"recordConfirm\");\n        AccountRecord accountRecord = accountRecordRepository.findById(accountId);\n        accountRecord.setStatusId(AccountStatus.NORMAL.getId());\n    }\n\n    @Override\n    public void recordCancel(TransactionContext transactionContext, long accountId, int amount) {\n        System.out.println(\"recordCancel\");\n\n        if (UnitTest.TRYING_EXCEPTION) {\n            throw new RuntimeException(\"record cancel failed.\");\n        }\n\n        AccountRecord accountRecord = accountRecordRepository.findById(accountId);\n        accountRecord.setBalanceAmount(accountRecord.getBalanceAmount() - amount);\n        accountRecord.setStatusId(AccountStatus.NORMAL.getId());\n\n\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/java/io/tyloo/unittest/utils/UnitTest.java",
    "content": "package io.tyloo.unittest.utils;\n\n\npublic class UnitTest {\n\n    public static volatile boolean CONFIRMING_EXCEPTION = false;\n    public static volatile boolean TRYING_EXCEPTION = false;\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/main/resources/tyloo-unit-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <context:component-scan base-package=\"io.tyloo.unittest\"/>\n\n\n    <bean class=\"io.tyloo.spring.recover.DefaultTylooRecoverConfiguration\">\n        <property name=\"maxRetryCount\" value=\"30\"/>\n        <property name=\"recoverDuration\" value=\"5\"/>\n        <property name=\"cronExpression\" value=\"0/5 * * * * ?\"/>\n    </bean>\n\n    <!--<bean id=\"transactionRepository\"-->\n          <!--class=\"SpringJdbcTransactionRepository\">-->\n        <!--<property name=\"dataSource\" ref=\"tylooTccDataSource\"/>-->\n        <!--<property name=\"domain\" value=\"UT\"/>-->\n        <!--<property name=\"tbSuffix\" value=\"_UT\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"transactionSerializer\" class=\"JdkSerializationSerializer\"/>-->\n\n\n    <!--<bean id=\"tylooTccDataSource\" class=\"org.apache.commons.dbcp.BasicDataSource\"-->\n          <!--destroy-method=\"close\">-->\n        <!--<property name=\"driverClassName\" value=\"com.mysql.jdbc.Driver\"/>-->\n        <!--<property name=\"url\" value=\"jdbc:mysql://localhost:3306/TCC\"/>-->\n        <!--<property name=\"username\" value=\"root\"/>-->\n        <!--<property name=\"password\" value=\"root\"/>-->\n    <!--</bean>-->\n\n    <!--<bean id=\"transactionRepository\"-->\n    <!--class=\"io.tyloo.repository.ZooKeeperTransactionRepository\">-->\n    <!--&lt;!&ndash;<property name=\"zkServers\" value=\"localhost:2181,localhost:2183,localhost:2185\"/>&ndash;&gt;-->\n    <!--<property name=\"zkServers\" value=\"localhost:2181\"/>-->\n    <!--<property name=\"zkTimeout\" value=\"10000\"/>-->\n    <!--<property name=\"zkRootPath\" value=\"/tcc_ut\"/>-->\n    <!--</bean>-->\n\n\n    <!--recommanded way; to use this, make sure redis is safely durable,which means need set redis as AOF mode and always fsync.\n      appendonly yes\n      appendfsync always\n      -->\n    <bean id=\"transactionRepository\" class=\"io.tyloo.repository.RedisTransactionRepository\">\n    <property name=\"keyPrefix\" value=\"tcc:ut:\"/>\n    <property name=\"jedisPool\" ref=\"jedisPool\"/>\n    </bean>\n\n    <bean id=\"jedisPoolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">\n    <property name=\"maxTotal\" value=\"1000\"/>\n    <property name=\"maxWaitMillis\" value=\"1000\"/>\n    </bean>\n\n    <bean id=\"jedisPool\" class=\"redis.clients.jedis.JedisPool\">\n    <constructor-arg index=\"0\" ref=\"jedisPoolConfig\"/>\n    <constructor-arg index=\"1\" value=\"127.0.0.1\"/>\n    <constructor-arg index=\"2\" value=\"6379\" type=\"int\"/>\n    <constructor-arg index=\"3\" value=\"1000\" type=\"int\"/>\n    <!--<constructor-arg index=\"4\" value=\"${redis.password}\"/>-->\n    </bean>\n\n    <!--<bean id=\"transactionRepository\" class=\"FileSystemTransactionRepository\">-->\n    <!--<property name=\"rootPath\" value=\"/data/tcc\"/>-->\n    <!--</bean>-->\n\n</beans>"
  },
  {
    "path": "tyloo-unit-test/src/test/java/io/tyloo/unit/test/AbstractTestCase.java",
    "content": "package io.tyloo.unit.test;\n\nimport org.junit.runner.RunWith;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\n\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(locations = {\n        \"classpath:/tyloo-unit-test.xml\",\"classpath:/tyloo.xml\"})\npublic abstract class AbstractTestCase {\n\n}"
  },
  {
    "path": "tyloo-unit-test/src/test/java/io/tyloo/unit/test/PerformanceTest.java",
    "content": "package io.tyloo.unit.test;\n\nimport io.tyloo.Participant;\nimport io.tyloo.Transaction;\nimport io.tyloo.common.TransactionType;\nimport io.tyloo.serializer.KryoPoolSerializer;\nimport io.tyloo.serializer.ObjectSerializer;\nimport io.tyloo.unittest.client.TransferService;\nimport org.junit.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.*;\n\n\npublic class PerformanceTest extends AbstractTestCase {\n\n    @Autowired\n    private TransferService transferService;\n\n    @Test\n    public void performanceTest() {\n\n        long currentTime = System.currentTimeMillis();\n\n        for (int i = 0; i < 1000; i++) {\n            transferService.performenceTuningTransfer();\n        }\n\n        long thenTime = System.currentTimeMillis();\n\n        System.out.println(thenTime - currentTime);\n    }\n\n    @Test\n    public void serializeTest() {\n\n        ObjectSerializer objectSerializer = new KryoPoolSerializer();\n\n        long currentTime = System.currentTimeMillis();\n\n        for (int i = 0; i < 10000; i++) {\n//\n            Transaction transaction = new Transaction(TransactionType.ROOT);\n            transaction.getAttachments().put(\"abc\", new Participant());\n            byte[] bytes = objectSerializer.serialize(transaction);\n            Transaction transaction1 = (Transaction) objectSerializer.deserialize(bytes);\n\n            if (transaction.getVersion() != transaction1.getVersion()) {\n                throw new Error();\n            }\n        }\n        long thenTime = System.currentTimeMillis();\n\n        System.out.println(thenTime - currentTime);\n    }\n\n    @Test\n    public void testThreadPool() throws ExecutionException, InterruptedException {\n\n        ExecutorService executorService = new ThreadPoolExecutor(1, 2,\n                30L, TimeUnit.MILLISECONDS,\n                new SynchronousQueue<Runnable>());\n\n        Long startTime = System.currentTimeMillis();\n\n        List<Future> futures = new ArrayList<Future>();\n\n        for (int i = 0; i <= 1; i++) {\n            futures.add(executorService.submit(new Runnable() {\n                @Override\n                public void run() {\n                    System.out.println(Thread.currentThread().getName());\n\n                    System.out.println(Thread.currentThread().getName() + \" done\");\n                }\n            }));\n        }\n\n        for (Future future : futures) {\n            future.get();\n        }\n\n        System.out.println(\"cost time:\" + (System.currentTimeMillis() - startTime));\n\n    }\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/test/java/io/tyloo/unit/test/ReflectionTest.java",
    "content": "package io.tyloo.unit.test;\n\nimport io.tyloo.Transaction;\nimport io.tyloo.serializer.JacksonJsonSerializer;\nimport io.tyloo.unittest.client.TransferService;\nimport io.tyloo.unittest.entity.SubAccount;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.lang.reflect.Method;\n\n\npublic class ReflectionTest extends AbstractTestCase {\n\n    @Autowired\n    private TransferService transferService;\n\n    @Test\n    public void test1() throws NoSuchMethodException {\n\n        transferService.performenceTuningTransfer();\n\n        Method transferMethod = TransferService.class.getMethod(\"transfer\", long.class, long.class, int.class);\n\n\n    }\n\n    @Test\n    public void testJacksonSerializer() {\n\n        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\\\",{}]}]\";\n\n        JacksonJsonSerializer jacksonJsonSerializer = new JacksonJsonSerializer();\n\n        Transaction transaction = jacksonJsonSerializer.deserialize(json.getBytes());\n\n        SubAccount subAccount = new SubAccount(1l, 10);\n\n        byte[] bytes = jacksonJsonSerializer.serialize(transaction);\n\n        json = new String(bytes);\n\n        transaction = jacksonJsonSerializer.deserialize(bytes);\n\n        Assert.assertTrue(transaction != null);\n    }\n\n}\n"
  },
  {
    "path": "tyloo-unit-test/src/test/java/io/tyloo/unit/test/TransferServiceTest.java",
    "content": "package io.tyloo.unit.test;\n\nimport io.tyloo.SystemException;\nimport io.tyloo.recover.TylooTransactionRecovery;\nimport io.tyloo.unittest.client.TransferService;\nimport io.tyloo.unittest.entity.AccountRecord;\nimport io.tyloo.unittest.entity.AccountStatus;\nimport io.tyloo.unittest.entity.SubAccount;\nimport io.tyloo.unittest.repository.AccountRecordRepository;\nimport io.tyloo.unittest.repository.SubAccountRepository;\nimport io.tyloo.unittest.utils.UnitTest;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n\npublic class TransferServiceTest extends AbstractTestCase {\n\n    @Autowired\n    private TransferService transferService;\n\n    @Autowired\n    SubAccountRepository subAccountRepository;\n\n    @Autowired\n    AccountRecordRepository accountRecordRepository;\n\n    @Autowired\n    TylooTransactionRecovery tylooTransactionRecovery;\n\n    @Test\n    public void testTransfer() throws InterruptedException {\n\n        //given\n        buildAccount();\n\n        //when\n        transferService.transfer(1, 2, 50);\n\n        //then\n        SubAccount subAccountFrom = subAccountRepository.findById(1L);\n\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n\n        Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());\n        Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());\n\n        Assert.assertTrue(subAccountFrom.getBalanceAmount() == 50);\n        Assert.assertTrue(subAccountTo.getBalanceAmount() == 250);\n    }\n\n    @Test\n    public void testTransferWithMandatoryPropagtion() throws InterruptedException {\n\n        //given\n        buildAccount();\n\n        //when\n        try {\n            transferService.transferWithMandatoryPropagation(1, 2, 50);\n        } catch (SystemException e) {\n            Assert.assertTrue(e.getMessage().startsWith(\"no active tyloo transaction while propagation is mandatory for method\"));\n        }\n    }\n\n    @Test\n    public void testTransferWithMultipleTier() {\n\n        //given\n        buildAccount();\n\n        //when\n        transferService.transferWithMultipleTier(1, 2, 50);\n\n        //then\n        SubAccount subAccountFrom = subAccountRepository.findById(1L);\n\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n\n        Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());\n        Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());\n\n        Assert.assertTrue(subAccountFrom.getBalanceAmount() == 50);\n        Assert.assertTrue(subAccountTo.getBalanceAmount() == 250);\n\n\n        AccountRecord accountRecordTo = accountRecordRepository.findById(2L);\n\n        Assert.assertTrue(accountRecordTo.getBalanceAmount() == 50);\n        Assert.assertTrue(accountRecordTo.getStatusId() == AccountStatus.NORMAL.getId());\n    }\n\n    @Test\n    public void testTransferWithMultiplerConsumer() {\n        //given\n        buildAccount();\n\n        //when\n        transferService.transferWithMultipleConsumer(1, 2, 70);\n\n        //then\n        SubAccount subAccountFrom = subAccountRepository.findById(1L);\n\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n\n        Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());\n        Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());\n\n        Assert.assertTrue(subAccountFrom.getBalanceAmount() == 30);\n        Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);\n    }\n\n    @Test\n    public void testTransferWithOnlyTryAndMultipleConsumer() {\n        //given\n        buildAccount();\n\n        //when\n        transferService.transferWithOnlyTryAndMultipleConsumer(1, 2, 70);\n\n        //then\n        SubAccount subAccountFrom = subAccountRepository.findById(1L);\n\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n\n        Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());\n        Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());\n\n        Assert.assertTrue(subAccountFrom.getBalanceAmount() == 30);\n        Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);\n    }\n\n    @Test\n    public void testTransferWithNoTransactionContext() {\n        //given\n        buildAccount();\n\n        //when\n        transferService.transferWithNoTransactionContext(1, 2, 70);\n\n        //then\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n\n        Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());\n        Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);\n    }\n\n\n    @Test\n    public void testTryingRecovery() {\n\n        //given\n        UnitTest.TRYING_EXCEPTION = true;\n\n        try {\n            //given\n            buildAccount();\n\n            //when\n            transferService.transferWithMultipleConsumer(1, 2, 70);\n\n        } catch (Throwable e) {\n\n        }\n\n        System.out.println(\"begin recovery\");\n\n        //when\n        UnitTest.TRYING_EXCEPTION = false;\n\n        //then\n        AccountRecord accountRecord = accountRecordRepository.findById(1L);\n        Assert.assertTrue(accountRecord.getBalanceAmount() == 70);\n\n        try {\n            //waiting the auto recovery schedule\n            Thread.sleep(1000 * 10);\n        } catch (InterruptedException e) {\n            throw new Error(e);\n        }\n        Assert.assertTrue(accountRecord.getBalanceAmount() == 0);\n\n    }\n\n    @Test\n    public void testConfirmingRecovery() {\n\n        //given\n        UnitTest.CONFIRMING_EXCEPTION = true;\n\n        try {\n            //given\n            buildAccount();\n\n            //when\n            transferService.transferWithMultipleConsumer(1, 2, 70);\n\n        } catch (Throwable e) {\n\n        }\n\n        System.out.println(\"begin recovery\");\n\n        //when\n        UnitTest.CONFIRMING_EXCEPTION = false;\n\n        //then\n        AccountRecord accountRecord = accountRecordRepository.findById(1L);\n        Assert.assertTrue(accountRecord.getBalanceAmount() == 70);\n\n        try {\n            //waiting the auto recovery schedule\n            Thread.sleep(1000 * 10L);\n        } catch (InterruptedException e) {\n            throw new Error(e);\n        }\n\n        Assert.assertTrue(accountRecord.getBalanceAmount() == 70);\n\n        SubAccount subAccountFrom = subAccountRepository.findById(1L);\n\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n\n        Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());\n        Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());\n\n        Assert.assertTrue(subAccountFrom.getBalanceAmount() == 30);\n        Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);\n\n\n    }\n\n\n    private void buildAccount() {\n        SubAccount subAccountFrom = subAccountRepository.findById(1L);\n\n        subAccountFrom.setBalanceAmount(100);\n        subAccountFrom.setStatus(AccountStatus.NORMAL.getId());\n\n        SubAccount subAccountTo = subAccountRepository.findById(2L);\n        subAccountTo.setBalanceAmount(200);\n        subAccountTo.setStatus(AccountStatus.NORMAL.getId());\n\n        AccountRecord accountRecordFrom = accountRecordRepository.findById(1L);\n        accountRecordFrom.setBalanceAmount(0);\n        accountRecordFrom.setStatusId(AccountStatus.NORMAL.getId());\n\n        AccountRecord accountRecordTo = accountRecordRepository.findById(2L);\n        accountRecordTo.setBalanceAmount(0);\n        accountRecordTo.setStatusId(AccountStatus.NORMAL.getId());\n    }\n}"
  },
  {
    "path": "tyloo-unit-test/src/test/resources/log4j.properties",
    "content": "# Set root logger level to DEBUG and its only appender to stdout.\nlog4j.rootLogger=INFO, stdout\n\n# stdout is set to be a ConsoleAppender.\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\n\n# stdout uses PatternLayout.\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n\n\n#log4j.appender.stdout.Target=System.out\n\nlog4j.logger.com.ibatis=debug,stdout\nlog4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug,stdout\nlog4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug,stdout\nlog4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug,stdout\nlog4j.logger.java.sql.Connection=debug,stdout\nlog4j.logger.java.sql.Statement=debug,stdout\nlog4j.logger.java.sql.PreparedStatement=debug,stdout "
  }
]