You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ni...@apache.org on 2018/09/12 05:40:37 UTC

[incubator-servicecomb-saga] branch master updated (1b000e5 -> 4a2daf4)

This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git.


    from 1b000e5  SCB-820 Updated the README.md
     new c8ee360  SCB-819 Only register the success participated event
     new 99a4298  SCB-819 Update the TODO document for the GrpcCoordinateStreamObserver
     new 456aac9  SCB-819 updated the tcc-spring-demo to meet the need of accept test
     new 4a2daf4  SCB-819 Added the accept test of tcc-spring-demo

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../pom.xml                                        | 79 ++++++-------------
 .../org/apache/servicecomb/saga/PackStepdefs.java  | 91 +++++++++-------------
 .../org/apache/servicecomb/saga/RunCucumberIT.java |  0
 .../src/test/resources/log4j2-test.xml             |  0
 .../resources/pack_tcc_cancel_scenario.feature}    | 29 +++----
 .../resources/pack_tcc_confirm_scenario.feature}   | 29 +++----
 acceptance-tests/pom.xml                           |  1 +
 .../tcc/registry/TransactionEventRegistry.java     | 24 +++---
 .../grpc/GrpcCoordinateStreamObserver.java         |  2 +-
 saga-demo/tcc-spring-demo/README.md                | 12 +--
 .../saga/demo/pack/inventory/InventoryService.java |  6 +-
 .../demo/pack/inventory/ProductController.java     | 18 ++---
 .../saga/demo/pack/inventory/ProductOrder.java     | 10 +--
 .../demo/pack/inventory/InventoryServiceTest.java  |  8 +-
 .../demo/pack/inventory/ProductControllerTest.java | 13 +++-
 .../demo/pack/ordering/OrderingController.java     |  4 +-
 .../saga/demo/pack/payment/PaymentController.java  |  8 +-
 .../demo/pack/payment/PaymentControllerTest.java   |  9 ++-
 18 files changed, 146 insertions(+), 197 deletions(-)
 copy acceptance-tests/{acceptance-pack-spring-demo => acceptance-pack-tcc-spring-demo}/pom.xml (80%)
 copy acceptance-tests/{acceptance-pack-spring-demo => acceptance-pack-tcc-spring-demo}/src/test/java/org/apache/servicecomb/saga/PackStepdefs.java (62%)
 copy acceptance-tests/{acceptance-pack-spring-demo => acceptance-pack-tcc-spring-demo}/src/test/java/org/apache/servicecomb/saga/RunCucumberIT.java (100%)
 copy {saga-spring => acceptance-tests/acceptance-pack-tcc-spring-demo}/src/test/resources/log4j2-test.xml (100%)
 copy acceptance-tests/{acceptance-pack-dubbo-demo/src/test/resources/Ab_success_scenario.feature => acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_cancel_scenario.feature} (57%)
 copy acceptance-tests/{acceptance-pack-dubbo-demo/src/test/resources/Ab_success_scenario.feature => acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_confirm_scenario.feature} (57%)


[incubator-servicecomb-saga] 03/04: SCB-819 updated the tcc-spring-demo to meet the need of accept test

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git

commit 456aac918cfa39ed8a19cf47bfa5d5366a84499f
Author: Willem Jiang <ji...@huawei.com>
AuthorDate: Tue Sep 11 18:29:51 2018 +0800

    SCB-819 updated the tcc-spring-demo to meet the need of accept test
---
 acceptance-tests/pom.xml                               |  1 +
 saga-demo/tcc-spring-demo/README.md                    | 12 ++++++------
 .../saga/demo/pack/inventory/InventoryService.java     |  6 +++---
 .../saga/demo/pack/inventory/ProductController.java    | 18 ++++++++----------
 .../saga/demo/pack/inventory/ProductOrder.java         | 10 +++++-----
 .../saga/demo/pack/inventory/InventoryServiceTest.java |  8 ++++----
 .../demo/pack/inventory/ProductControllerTest.java     | 13 ++++++++++---
 .../saga/demo/pack/ordering/OrderingController.java    |  4 ++--
 .../saga/demo/pack/payment/PaymentController.java      |  8 ++++----
 .../saga/demo/pack/payment/PaymentControllerTest.java  |  9 ++++++++-
 10 files changed, 51 insertions(+), 38 deletions(-)

diff --git a/acceptance-tests/pom.xml b/acceptance-tests/pom.xml
index 9f8bfea..d6dc00b 100644
--- a/acceptance-tests/pom.xml
+++ b/acceptance-tests/pom.xml
@@ -32,6 +32,7 @@
   <modules>
     <module>acceptance-pack-dubbo-demo</module>
     <module>acceptance-pack-spring-demo</module>
+    <module>acceptance-pack-tcc-spring-demo</module>
   </modules>
 
   <properties>
diff --git a/saga-demo/tcc-spring-demo/README.md b/saga-demo/tcc-spring-demo/README.md
index 5c8704c..07e407b 100644
--- a/saga-demo/tcc-spring-demo/README.md
+++ b/saga-demo/tcc-spring-demo/README.md
@@ -94,29 +94,29 @@ You can run the demo using either docker compose or executable files.
 ## User Requests by command line tools
 1. Ordering 2 units ProductA with the unit price 1 from UserA account, this ordering will be OK.
 ```
-curl -X POST http://${host_address}:8083/ordering/order/UserA/ProductA/3/1
+curl -X POST http://${host_address}:8083/order/UserA/ProductA/3/1
 ```
 Check the Inventory orders status with
 ```
-curl http://${host_address}:8081/products/orders
+curl http://${host_address}:8081/orderings
 ```
 Check the Payment transaction status with
 ```
-curl http://${host_address}:8082/payments/transactions
+curl http://${host_address}:8082/transactions
 
 ```
 
 2. Ordering 2 units of ProductA with the unit price 2 from UserB account , this ordering will cause the payment failed and trigger the cancel operation with inventory ordering.
 ```
-curl -X POST http://${host_address}:8083/ordering/order/UserB/ProductA/3/1
+curl -X POST http://${host_address}:8083/order/UserB/ProductA/3/1
 ```
 Check the hotel booking status with
 ```
-curl http://${host_address}:8081/products/orders
+curl http://${host_address}:8081/orderings
 ```
 Check the car booking status with
 ```
-curl http://${host_address}:8082/payments/transactions
+curl http://${host_address}:8082/transactions
 ```
 The second car booking will be marked with **cancel:true**
 
diff --git a/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryService.java b/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryService.java
index 37e97c8..29b2b72 100644
--- a/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryService.java
+++ b/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryService.java
@@ -38,8 +38,8 @@ public class InventoryService {
   @Transactional
   public void reserve(ProductOrder order) {
     Product product = getProduct(order.getProductName());
-    if (product.getInStock() > order.getAmount()) {
-      product.setInStock(product.getInStock() - order.getAmount());
+    if (product.getInStock() > order.getUnits()) {
+      product.setInStock(product.getInStock() - order.getUnits());
       productDao.saveAndFlush(product);
       orders.put(order.getId(), order);
     } else {
@@ -54,7 +54,7 @@ public class InventoryService {
   @Transactional
   public void cancel(ProductOrder order) {
     Product product = productDao.findProduceByName(order.getProductName());
-    product.setInStock(product.getInStock() + order.getAmount());
+    product.setInStock(product.getInStock() + order.getUnits());
     productDao.saveAndFlush(product);
     order.setCancelled(true);
   }
diff --git a/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductController.java b/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductController.java
index 076721f..e55ca52 100644
--- a/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductController.java
+++ b/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductController.java
@@ -22,15 +22,12 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-@RequestMapping("/products")
 @Controller
 public class ProductController {
   @Autowired
@@ -38,30 +35,31 @@ public class ProductController {
 
   private final AtomicInteger id = new AtomicInteger(0);
 
-  @PostMapping("/order/{userName}/{productName}/{amount}")
+  @PostMapping("/order/{userName}/{productName}/{units}")
   @ResponseBody
   public ProductOrder updateInventory(@PathVariable String userName,
-      @PathVariable String productName, @PathVariable Integer amount) {
+      @PathVariable String productName, @PathVariable Integer units) {
     ProductOrder order = new ProductOrder();
     order.setId(id.incrementAndGet());
     order.setUserName(userName);
     order.setProductName(productName);
-    order.setAmount(amount);
+    order.setUnits(units);
     inventoryService.reserve(order);
     return order;
   }
 
-  @CrossOrigin
-  @GetMapping("/orders")
+  @GetMapping("/orderings")
   @ResponseBody
   List<ProductOrder> getAll() {
     return new ArrayList<>(inventoryService.getAllOrders());
   }
 
-  @DeleteMapping("/orders")
-  void clear() {
+  @DeleteMapping("/orderings")
+  @ResponseBody
+  String clear() {
     inventoryService.clearAllOrders();
     id.set(0);
+    return "OK";
   }
 
 }
diff --git a/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductOrder.java b/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductOrder.java
index ebb074e..5ef6863 100644
--- a/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductOrder.java
+++ b/saga-demo/tcc-spring-demo/inventory/src/main/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductOrder.java
@@ -27,7 +27,7 @@ public class ProductOrder {
   private Integer id;
   private String userName;
   private String productName;
-  private Integer amount;
+  private Integer units;
   private boolean confirmed;
   private boolean cancelled;
 
@@ -48,12 +48,12 @@ public class ProductOrder {
     this.productName = productName;
   }
 
-  public Integer getAmount() {
-    return amount;
+  public Integer getUnits() {
+    return units;
   }
 
-  public void setAmount(Integer amount) {
-    this.amount = amount;
+  public void setUnits(Integer units) {
+    this.units = units;
   }
 
   public boolean isConfirmed() {
diff --git a/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryServiceTest.java b/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryServiceTest.java
index 0842f2c..1f49734 100644
--- a/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryServiceTest.java
+++ b/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/InventoryServiceTest.java
@@ -61,7 +61,7 @@ public class InventoryServiceTest {
   public void testInventoryServiceWithConfirmation() {
     order.setId(1);
     order.setUserName("user1");
-    order.setAmount(10);
+    order.setUnits(10);
     order.setProductName("ProductA");
     inventoryService.reserve(order);
     assertThat(inventoryService.getInventory("ProductA"), is(90));
@@ -76,7 +76,7 @@ public class InventoryServiceTest {
   @Test
   public void getNoExitProductFromInventoryService() {
     order.setId(1);
-    order.setAmount(10);
+    order.setUnits(10);
     order.setProductName("Product");
     try {
       inventoryService.reserve(order);
@@ -89,7 +89,7 @@ public class InventoryServiceTest {
 
   @Test
   public void getProductOutOfStockFromInventoryService() {
-    order.setAmount(10);
+    order.setUnits(10);
     order.setProductName("ProductC");
     try {
       inventoryService.reserve(order);
@@ -103,7 +103,7 @@ public class InventoryServiceTest {
   @Test
   public void testInventoryServiceWithCancel() {order.setUserName("user1");
     order.setId(1);
-    order.setAmount(10);
+    order.setUnits(10);
     order.setProductName("ProductA");
     inventoryService.reserve(order);
     assertThat(inventoryService.getInventory("ProductA"), is(90));
diff --git a/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductControllerTest.java b/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductControllerTest.java
index 8dcc7dd..6490ad9 100644
--- a/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductControllerTest.java
+++ b/saga-demo/tcc-spring-demo/inventory/src/test/java/org/apache/servicecomb/saga/demo/pack/inventory/ProductControllerTest.java
@@ -21,6 +21,7 @@ import static java.util.Collections.singletonList;
 import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
 import static org.hamcrest.core.Is.is;
 import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -53,14 +54,20 @@ public class ProductControllerTest {
 
   @Test
   public void retrievesOrdersFromRepo() throws Exception {
-    mockMvc.perform(get("/products/orders"))
+    mockMvc.perform(get("/orderings"))
         .andExpect(status().isOk())
         .andExpect(jsonPath("$", hasSize(1)))
         .andExpect(jsonPath("$.[0].userName", is(someOrder.getUserName())))
         .andExpect(jsonPath("$.[0].productName", is(someOrder.getProductName())))
         .andExpect(jsonPath("$.[0].confirmed", is(someOrder.isConfirmed())))
         .andExpect(jsonPath("$.[0].cancelled", is(someOrder.isCancelled())))
-        .andExpect(jsonPath("$.[0].amount", is(someOrder.getAmount())));
+        .andExpect(jsonPath("$.[0].units", is(someOrder.getUnits())));
+  }
+
+  @Test
+  public void verifyDeletingOrders() throws Exception {
+    mockMvc.perform(delete("/orderings"))
+        .andExpect(status().isOk());
   }
 
   private ProductOrder someOrder() {
@@ -68,7 +75,7 @@ public class ProductControllerTest {
     order.setId(1);
     order.setProductName("ProductName1");
     order.setUserName("UserName");
-    order.setAmount(100);
+    order.setUnits(100);
     order.setCancelled(false);
     order.setConfirmed(true);
     return order;
diff --git a/saga-demo/tcc-spring-demo/ordering/src/main/java/org/apache/servicecomb/saga/demo/pack/ordering/OrderingController.java b/saga-demo/tcc-spring-demo/ordering/src/main/java/org/apache/servicecomb/saga/demo/pack/ordering/OrderingController.java
index a19da78..5affe76 100644
--- a/saga-demo/tcc-spring-demo/ordering/src/main/java/org/apache/servicecomb/saga/demo/pack/ordering/OrderingController.java
+++ b/saga-demo/tcc-spring-demo/ordering/src/main/java/org/apache/servicecomb/saga/demo/pack/ordering/OrderingController.java
@@ -46,12 +46,12 @@ public class OrderingController {
       @PathVariable String productName, @PathVariable Integer productUnit, @PathVariable Integer unitPrice) {
 
     restTemplate.postForEntity(
-        inventoryServiceUrl + "/products/order/{userName}/{productName}/{productUnit}",
+        inventoryServiceUrl + "/order/{userName}/{productName}/{productUnit}",
         null, String.class, userName, productName, productUnit);
 
     int amount = productUnit * unitPrice;
     
-    restTemplate.postForEntity(paymentServiceUrl + "/payments/pay/{userName}/{amount}",
+    restTemplate.postForEntity(paymentServiceUrl + "/pay/{userName}/{amount}",
         null, String.class, userName, amount);
 
     return userName + " ordering " + productName + " with " + productUnit + " OK";
diff --git a/saga-demo/tcc-spring-demo/payment/src/main/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentController.java b/saga-demo/tcc-spring-demo/payment/src/main/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentController.java
index d647bf5..3f74ed5 100644
--- a/saga-demo/tcc-spring-demo/payment/src/main/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentController.java
+++ b/saga-demo/tcc-spring-demo/payment/src/main/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentController.java
@@ -30,7 +30,6 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-@RequestMapping("/payments")
 @Controller
 public class PaymentController {
   @Autowired
@@ -49,8 +48,7 @@ public class PaymentController {
     paymentService.pay(payment);
     return payment;
   }
-
-  @CrossOrigin
+  
   @GetMapping("/transactions")
   @ResponseBody
   List<Payment> getAll() {
@@ -58,9 +56,11 @@ public class PaymentController {
   }
 
   @DeleteMapping("/transactions")
-  void clear() {
+  @ResponseBody
+  String clear() {
     paymentService.clearAllTransactions();
     id.set(0);
+    return "OK";
   }
 
 }
diff --git a/saga-demo/tcc-spring-demo/payment/src/test/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentControllerTest.java b/saga-demo/tcc-spring-demo/payment/src/test/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentControllerTest.java
index 6adbf7c..eb8b158 100644
--- a/saga-demo/tcc-spring-demo/payment/src/test/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentControllerTest.java
+++ b/saga-demo/tcc-spring-demo/payment/src/test/java/org/apache/servicecomb/saga/demo/pack/payment/PaymentControllerTest.java
@@ -21,6 +21,7 @@ import static java.util.Collections.singletonList;
 import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
 import static org.hamcrest.core.Is.is;
 import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -53,7 +54,7 @@ public class PaymentControllerTest {
 
   @Test
   public void retrievesPaymentsFromRepo() throws Exception {
-    mockMvc.perform(get("/payments/transactions"))
+    mockMvc.perform(get("/transactions"))
         .andExpect(status().isOk())
         .andExpect(jsonPath("$", hasSize(1)))
         .andExpect(jsonPath("$.[0].userName", is(somePayment.getUserName())))
@@ -63,6 +64,12 @@ public class PaymentControllerTest {
         .andExpect(jsonPath("$.[0].amount", is(somePayment.getAmount())));
   }
 
+  @Test
+  public void verifyDeletingOrders() throws Exception {
+    mockMvc.perform(delete("/transactions"))
+        .andExpect(status().isOk());
+  }
+
   private Payment somePayment() {
     Payment payment = new Payment();
     payment.setId(1);


[incubator-servicecomb-saga] 02/04: SCB-819 Update the TODO document for the GrpcCoordinateStreamObserver

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git

commit 99a42987ee1d6f423894223088e0c71a9a703c5d
Author: Willem Jiang <ji...@huawei.com>
AuthorDate: Tue Sep 11 18:29:12 2018 +0800

    SCB-819 Update the TODO document for the GrpcCoordinateStreamObserver
---
 .../saga/omega/connector/grpc/GrpcCoordinateStreamObserver.java         | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/omega/omega-connector/omega-connector-grpc/src/main/java/org/apache/servicecomb/saga/omega/connector/grpc/GrpcCoordinateStreamObserver.java b/omega/omega-connector/omega-connector-grpc/src/main/java/org/apache/servicecomb/saga/omega/connector/grpc/GrpcCoordinateStreamObserver.java
index 20f5974..31943b8 100644
--- a/omega/omega-connector/omega-connector-grpc/src/main/java/org/apache/servicecomb/saga/omega/connector/grpc/GrpcCoordinateStreamObserver.java
+++ b/omega/omega-connector/omega-connector-grpc/src/main/java/org/apache/servicecomb/saga/omega/connector/grpc/GrpcCoordinateStreamObserver.java
@@ -45,7 +45,7 @@ public class GrpcCoordinateStreamObserver implements StreamObserver<GrpcTccCoord
 
   @Override
   public void onError(Throwable t) {
-    //TODO need to find a way to handle the error
+    //TODO need to find a way to handle the error and create connection again
     LOG.error("Failed to process grpc coordinate command.", t);
   }
 


[incubator-servicecomb-saga] 04/04: SCB-819 Added the accept test of tcc-spring-demo

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git

commit 4a2daf4a7b020f7eb4c5eb6cdfc60abe10dc03ac
Author: Willem Jiang <ji...@huawei.com>
AuthorDate: Tue Sep 11 18:30:47 2018 +0800

    SCB-819 Added the accept test of tcc-spring-demo
---
 .../acceptance-pack-tcc-spring-demo/pom.xml        | 351 +++++++++++++++++++++
 .../org/apache/servicecomb/saga/PackStepdefs.java  | 190 +++++++++++
 .../org/apache/servicecomb/saga/RunCucumberIT.java |  29 ++
 .../src/test/resources/log4j2-test.xml             |  30 ++
 .../resources/pack_tcc_cancel_scenario.feature     |  31 ++
 .../resources/pack_tcc_confirm_scenario.feature    |  33 ++
 6 files changed, 664 insertions(+)

diff --git a/acceptance-tests/acceptance-pack-tcc-spring-demo/pom.xml b/acceptance-tests/acceptance-pack-tcc-spring-demo/pom.xml
new file mode 100644
index 0000000..59bc1e1
--- /dev/null
+++ b/acceptance-tests/acceptance-pack-tcc-spring-demo/pom.xml
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>acceptance-tests</artifactId>
+    <groupId>org.apache.servicecomb.saga</groupId>
+    <version>0.3.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>acceptance-pack-tcc-spring-demo</artifactId>
+  <name>Saga:Acceptance Tests::Pack::TCC::Spring</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>${jackson.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>${jackson.version}</version>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>jdk9</id>
+      <activation>
+        <jdk>[9,)</jdk>
+      </activation>
+      <!-- TODO using jdk argLine to get rid of the jaxb dependencies -->
+      <properties>
+        <jdk.argLine>--add-modules java.xml.bind</jdk.argLine>
+      </properties>
+      <dependencies>
+        <dependency>
+          <groupId>javax.xml.bind</groupId>
+          <artifactId>jaxb-api</artifactId>
+          <version>${jaxb.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>com.sun.xml.bind</groupId>
+          <artifactId>jaxb-impl</artifactId>
+          <version>${jaxb.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.glassfish.jaxb</groupId>
+          <artifactId>jaxb-runtime</artifactId>
+          <version>${jaxb.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>javax.activation</groupId>
+          <artifactId>activation</artifactId>
+          <version>${javax.activation.version}</version>
+        </dependency>
+      </dependencies>
+    </profile>
+    <profile>
+      <id>docker</id>
+      <activation>
+        <file>
+          <exists>/var/run/docker.sock</exists>
+        </file>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>io.fabric8</groupId>
+            <artifactId>docker-maven-plugin</artifactId>
+            <configuration>
+              <images>
+                <image>
+                  <name>postgres</name>
+                  <alias>postgres</alias>
+                  <run>
+                    <env>
+                      <POSTGRES_DB>saga</POSTGRES_DB>
+                      <POSTGRES_USER>saga</POSTGRES_USER>
+                      <POSTGRES_PASSWORD>password</POSTGRES_PASSWORD>
+                    </env>
+                    <wait>
+                      <log>database system is ready to accept connections</log>
+                      <tcp>
+                        <ports>
+                          <port>5432</port>
+                        </ports>
+                      </tcp>
+                      <time>60000</time>
+                    </wait>
+                    <ports>
+                      <port>postgres.port:5432</port>
+                    </ports>
+                  </run>
+                </image>
+                <image>
+                  <name>alpha-server:${project.version}</name>
+                  <alias>alpha</alias>
+                  <run>
+                    <env>
+                      <JAVA_OPTS>
+                        -Dspring.profiles.active=prd -Dspring.datasource.initialization-mode=always
+                      </JAVA_OPTS>
+                    </env>
+                    <links>
+                      <link>postgres:postgresql.servicecomb.io</link>
+                    </links>
+                    <wait>
+                      <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log>
+                      <tcp>
+                        <ports>
+                          <port>8080</port>
+                          <port>8090</port>
+                        </ports>
+                      </tcp>
+                      <time>120000</time>
+                    </wait>
+                    <ports>
+                      <port>alpha.port:8080</port>
+                      <port>alpha.rest.port:8090</port>
+                    </ports>
+                    <dependsOn>
+                      <container>postgres</container>
+                    </dependsOn>
+                  </run>
+                </image>
+                <image>
+                  <name>tcc-inventory:${project.version}</name>
+                  <alias>inventory</alias>
+                  <run>
+                    <env>
+                      <JAVA_OPTS>
+                        -Dspring.profiles.active=prd
+                      </JAVA_OPTS>
+                    </env>
+                    <wait>
+                      <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log>
+                      <tcp>
+                        <ports>
+                          <port>8080</port>
+                        </ports>
+                      </tcp>
+                      <time>120000</time>
+                    </wait>
+                    <links>
+                      <link>alpha:alpha-server.servicecomb.io</link>
+                    </links>
+                    <ports>
+                      <port>inventory.port:8080</port>
+                    </ports>
+                    <dependsOn>
+                      <container>alpha</container>
+                    </dependsOn>
+                  </run>
+                </image>
+                <image>
+                  <name>tcc-payment:${project.version}</name>
+                  <alias>payment</alias>
+                  <run>
+                    <env>
+                      <JAVA_OPTS>
+                        -Dspring.profiles.active=prd
+                      </JAVA_OPTS>
+                    </env>
+                    <wait>
+                      <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log>
+                      <tcp>
+                        <ports>
+                          <port>8080</port>
+                        </ports>
+                      </tcp>
+                      <time>120000</time>
+                    </wait>
+                    <links>
+                      <link>alpha:alpha-server.servicecomb.io</link>
+                    </links>
+                    <ports>
+                      <port>payment.port:8080</port>
+                    </ports>
+                    <dependsOn>
+                      <container>alpha</container>
+                    </dependsOn>
+                  </run>
+                </image>
+                <image>
+                  <name>tcc-ordering:${project.version}</name>
+                  <alias>ordering</alias>
+                  <run>
+                    <wait>
+                      <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log>
+                      <tcp>
+                        <ports>
+                          <port>8080</port>
+                        </ports>
+                      </tcp>
+                      <time>120000</time>
+                    </wait>
+                    <links>
+                      <link>alpha:alpha-server.servicecomb.io</link>
+                      <link>inventory:inventory.servicecomb.io</link>
+                      <link>payment:payment.servicecomb.io</link>
+                    </links>
+                    <ports>
+                      <port>ordering.port:8080</port>
+                    </ports>
+                    <dependsOn>
+                      <container>alpha</container>
+                    </dependsOn>
+                  </run>
+                </image>
+              </images>
+            </configuration>
+            <executions>
+              <execution>
+                <id>start</id>
+                <phase>pre-integration-test</phase>
+                <goals>
+                  <goal>start</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>stop</id>
+                <phase>post-integration-test</phase>
+                <goals>
+                  <goal>stop</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.codehaus.gmaven</groupId>
+            <artifactId>gmaven-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>add-default-properties</id>
+                <phase>initialize</phase>
+                <goals>
+                  <goal>execute</goal>
+                </goals>
+                <configuration>
+                  <source>
+                    project.properties.setProperty('docker.hostname', 'localhost')
+                    log.info("Docker hostname is " + project.properties['docker.hostname'])
+                  </source>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-failsafe-plugin</artifactId>
+            <version>${maven.failsafe.version}</version>
+            <configuration>
+              <systemPropertyVariables>
+                <alpha.cluster.address>
+                  http://${docker.hostname}:${alpha.port}
+                </alpha.cluster.address>
+                <alpha.rest.address>
+                  http://${docker.hostname}:${alpha.rest.port}
+                </alpha.rest.address>
+                <inventory.service.address>
+                  http://${docker.hostname}:${inventory.port}
+                </inventory.service.address>
+                <payment.service.address>
+                  http://${docker.hostname}:${payment.port}
+                </payment.service.address>
+                <ordering.service.address>
+                  http://${docker.hostname}:${ordering.port}
+                </ordering.service.address>
+                <spring.datasource.url>
+                  jdbc:postgresql://${docker.hostname}:${postgres.port}/saga?useSSL=false
+                </spring.datasource.url>
+                <info.service.uri>
+                  ${info.service.uri}
+                </info.service.uri>
+              </systemPropertyVariables>
+              <argLine>${jacoco.failsafe.argLine}</argLine>
+            </configuration>
+            <executions>
+              <execution>
+                <goals>
+                  <goal>integration-test</goal>
+                  <goal>verify</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>com.ethlo.persistence.tools</groupId>
+            <artifactId>eclipselink-maven-plugin</artifactId>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>docker-machine</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.gmaven</groupId>
+            <artifactId>gmaven-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>add-dynamic-properties</id>
+                <phase>prepare-package</phase>
+                <goals>
+                  <goal>execute</goal>
+                </goals>
+                <configuration>
+                  <source>
+                    def process = "docker-machine ip default".execute()
+                    process.waitFor()
+                    project.properties.setProperty('docker.hostname', process.in.text.trim())
+
+                    log.info("Docker hostname is " + project.properties['docker.hostname'])
+                  </source>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>spring-boot-2</id>
+      <properties>
+        <info.service.uri>/actuator/info</info.service.uri>
+      </properties>
+    </profile>
+  </profiles>
+
+</project>
diff --git a/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/java/org/apache/servicecomb/saga/PackStepdefs.java b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/java/org/apache/servicecomb/saga/PackStepdefs.java
new file mode 100644
index 0000000..2a70d9b
--- /dev/null
+++ b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/java/org/apache/servicecomb/saga/PackStepdefs.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.saga;
+
+import static io.restassured.RestAssured.given;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.core.Is.is;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.restassured.response.Response;
+import cucumber.api.DataTable;
+import cucumber.api.java.After;
+import cucumber.api.java8.En;
+
+public class PackStepdefs implements En {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private static final String ALPHA_REST_ADDRESS = "alpha.rest.address";
+  private static final String INVENTORY_SERVICE_ADDRESS = "inventory.service.address";
+  private static final String PAYMENT_SERVICE_ADDRESS = "payment.service.address";
+  private static final String ORDERING_SERVICE_ADDRESS = "ordering.service.address";
+  private static final String INVENTORY_ORDERS_URI = "/orderings";
+  private static final String PAYMENT_ORDERS_URI = "/transactions";
+  private static final String INFO_SERVICE_URI = "info.service.uri";
+  private static final String[] addresses = {INVENTORY_SERVICE_ADDRESS, PAYMENT_SERVICE_ADDRESS};
+  private static final String[] uris = {INVENTORY_ORDERS_URI, PAYMENT_ORDERS_URI};
+
+  private static final Consumer<Map<String, String>[]> NO_OP_CONSUMER = (dataMap) -> {
+  };
+
+  public PackStepdefs() {
+    Given("^Inventory Service is up and running$", () -> {
+      probe(System.getProperty(INVENTORY_SERVICE_ADDRESS));
+    });
+
+    And("^Payment Service is up and running$", () -> {
+      probe(System.getProperty(PAYMENT_SERVICE_ADDRESS));
+    });
+
+    And("^Ordering Service is up and running$", () -> {
+      probe(System.getProperty(ORDERING_SERVICE_ADDRESS));
+    });
+
+    And("^Alpha is up and running$", () -> {
+      probe(System.getProperty(ALPHA_REST_ADDRESS));
+    });
+
+    
+    When("^User ([A-Za-z]+) requests to order ([0-9]+) units of ([A-Za-z]+) with unit price ([0-9]+) (success|fail)$",
+        (userName, productUnits, productName, unitPrice, result) -> {
+      LOG.info("Received request from user {} to order {} units of {}  with unit price {}", userName, productUnits, productName, unitPrice);
+
+      Response resp = given()
+          .pathParam("userName", userName)
+          .pathParam("productName", productName)
+          .pathParam("productUnits", productUnits)
+          .pathParam("unitPrice", unitPrice)
+          .when()
+          .post(System.getProperty(ORDERING_SERVICE_ADDRESS) + "/ordering/order/{userName}/{productName}/{productUnits}/{unitPrice}");
+      if (result.equals("success")) {
+        resp.then().statusCode(is(200));
+      } else if (result.equals("fail")) {
+        resp.then().statusCode(is(500));
+      }
+      // Need to wait for a while to let the confirm or cannel command finished.
+      Thread.sleep(2000);
+    });
+
+    //TODO need to check the events from the alpha
+    /*Then("^Alpha records the following events$", (DataTable dataTable) -> {
+      Consumer<Map<String, String>[]> columnStrippingConsumer = dataMap -> {
+        for (Map<String, String> map : dataMap)
+          map.keySet().retainAll(dataTable.topCells());
+      };
+
+      dataMatches(System.getProperty(ALPHA_REST_ADDRESS) + "/events", dataTable, columnStrippingConsumer);
+    });*/
+
+    Then("^Inventory Service contains the following booking orders$", (DataTable dataTable) -> {
+      dataMatches(System.getProperty(INVENTORY_SERVICE_ADDRESS) + INVENTORY_ORDERS_URI, dataTable, NO_OP_CONSUMER);
+    });
+
+    And("^Payment Service contains the following booking orders$", (DataTable dataTable) -> {
+      dataMatches(System.getProperty(PAYMENT_SERVICE_ADDRESS) + PAYMENT_ORDERS_URI, dataTable, NO_OP_CONSUMER);
+    });
+  }
+
+  @After
+  public void cleanUp() {
+    LOG.info("Cleaning up services");
+    for (int i = 0; i < addresses.length; i++) {
+      given()
+          .when()
+          .delete(System.getProperty(addresses[i]) + uris[i])
+          .then()
+          .statusCode(is(200));
+    }
+
+    /*given()
+        .when()
+        .delete(System.getProperty(ALPHA_REST_ADDRESS) + "/events")
+        .then()
+        .statusCode(is(200));*/
+
+  }
+
+  @SuppressWarnings("unchecked")
+  private void dataMatches(String address, DataTable dataTable, Consumer<Map<String, String>[]> dataProcessor) {
+    List<Map<String, String>> expectedMaps = dataTable.asMaps(String.class, String.class);
+    List<Map<String, String>> actualMaps = new ArrayList<>();
+
+    await().atMost(5, SECONDS).until(() -> {
+      actualMaps.clear();
+      Collections.addAll(actualMaps, retrieveDataMaps(address, dataProcessor));
+      // write the log if the Map size is not same
+      boolean result = expectedMaps.size() == actualMaps.size();
+      if (!result) {
+        LOG.warn("The response message size is not we expected. ExpectedMap size is {},  ActualMap size is {}", expectedMaps.size(), actualMaps.size());
+      }
+      return expectedMaps.size() == actualMaps.size();
+    });
+
+    if (expectedMaps.isEmpty() && actualMaps.isEmpty()) {
+      return;
+    }
+
+    LOG.info("Retrieved data {} from service", actualMaps);
+    dataTable.diff(DataTable.create(actualMaps));
+  }
+
+  @SuppressWarnings("unchecked")
+  private Map<String, String>[] retrieveDataMaps(String address, Consumer<Map<String, String>[]> dataProcessor) {
+    Map<String, String>[] dataMap = given()
+        .when()
+        .get(address)
+        .then()
+        .statusCode(is(200))
+        .extract()
+        .body()
+        .as(Map[].class);
+
+    dataProcessor.accept(dataMap);
+    return dataMap;
+  }
+
+  private void probe(String address) {
+    String infoURI = System.getProperty(INFO_SERVICE_URI);
+    if (isEmpty(infoURI)) {
+      infoURI = "/info";
+    }
+    LOG.info("The info service uri is " + infoURI);
+    probe(address, infoURI);
+  }
+
+  private void probe(String address, String infoURI) {
+    LOG.info("Connecting to service address {}", address);
+    given()
+        .when()
+        .get(address + infoURI)
+        .then()
+        .statusCode(is(200));
+  }
+
+}
diff --git a/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/java/org/apache/servicecomb/saga/RunCucumberIT.java b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/java/org/apache/servicecomb/saga/RunCucumberIT.java
new file mode 100644
index 0000000..d4fc9ee
--- /dev/null
+++ b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/java/org/apache/servicecomb/saga/RunCucumberIT.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.saga;
+
+import org.junit.runner.RunWith;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(plugin = {"pretty", "html:target/cucumber"},
+    features = "src/test/resources")
+public class RunCucumberIT {
+}
diff --git a/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/log4j2-test.xml b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000..58924c6
--- /dev/null
+++ b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/log4j2-test.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<Configuration status="WARN">
+  <Appenders>
+    <Console name="Console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+    </Console>
+  </Appenders>
+  <Loggers>
+    <Root level="info">
+      <AppenderRef ref="Console"/>
+    </Root>
+  </Loggers>
+</Configuration>
diff --git a/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_cancel_scenario.feature b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_cancel_scenario.feature
new file mode 100644
index 0000000..38fb9dd
--- /dev/null
+++ b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_cancel_scenario.feature
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+Feature: Alpha records transaction events
+
+  Scenario: A sub-transaction failed and global transaction compensated
+    Given Inventory Service is up and running
+    And Payment Service is up and running
+    And Ordering Service is up and running
+    And Alpha is up and running
+
+    When User UserC requests to order 2 units of ProductA with unit price 2 fail
+
+    Then Inventory Service contains the following booking orders
+      | userName | productName | units | confirmed | cancelled |
+      | UserC    | ProductA    |  2    | false     |  true     |
+
+    Then Payment Service contains the following booking orders
+      | userName | amount | balance | confirmed | cancelled |
diff --git a/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_confirm_scenario.feature b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_confirm_scenario.feature
new file mode 100644
index 0000000..87ba508
--- /dev/null
+++ b/acceptance-tests/acceptance-pack-tcc-spring-demo/src/test/resources/pack_tcc_confirm_scenario.feature
@@ -0,0 +1,33 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+Feature: Alpha records transaction events
+
+  Scenario: Everything is normal
+    Given Inventory Service is up and running
+    And Payment Service is up and running
+    And Ordering Service is up and running
+    And Alpha is up and running
+
+    When User UserA requests to order 2 units of ProductA with unit price 2 success
+
+
+    And Inventory Service contains the following booking orders
+      | userName | productName | units | confirmed | cancelled |
+      | UserA    | ProductA    |  2    | true      |  false    |
+
+    And Payment Service contains the following booking orders
+      | userName | amount | balance | confirmed | cancelled |
+      | UserA    |   4    |  96     | true      | false     |


[incubator-servicecomb-saga] 01/04: SCB-819 Only register the success participated event

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git

commit c8ee36004e47332d327c09649d7f25abe81f1400
Author: Willem Jiang <ji...@huawei.com>
AuthorDate: Tue Sep 11 18:28:09 2018 +0800

    SCB-819 Only register the success participated event
---
 .../tcc/registry/TransactionEventRegistry.java     | 24 +++++++++++++---------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/tcc/registry/TransactionEventRegistry.java b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/tcc/registry/TransactionEventRegistry.java
index d135a8b..db60195 100644
--- a/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/tcc/registry/TransactionEventRegistry.java
+++ b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/tcc/registry/TransactionEventRegistry.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import org.apache.servicecomb.saga.alpha.server.tcc.event.ParticipatedEvent;
+import org.apache.servicecomb.saga.common.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,18 +39,21 @@ public final class TransactionEventRegistry {
   /**
    * Register participate event.
    *
-   * @param participateEvent participate event
+   * @param participatedEvent participated event
    */
-  public static void register(ParticipatedEvent participateEvent) {
-    REGISTRY
-        .computeIfAbsent(participateEvent.getGlobalTxId(), key -> new LinkedHashSet<>())
-        .add(participateEvent);
+  public static void register(ParticipatedEvent participatedEvent) {
+    // Only register the Succeed participatedEvent
+    if (TransactionStatus.Succeed.equals(participatedEvent.getStatus())) {
+      REGISTRY
+          .computeIfAbsent(participatedEvent.getGlobalTxId(), key -> new LinkedHashSet<>())
+          .add(participatedEvent);
 
-    LOG.info("Registered participated event, global tx: {}, local tx: {}, parent id: {}, "
-            + "confirm: {}, cancel: {}, status: {}, service [{}] instanceId [{}]",
-        participateEvent.getGlobalTxId(), participateEvent.getLocalTxId(), participateEvent.getParentTxId(),
-        participateEvent.getConfirmMethod(), participateEvent.getCancelMethod(), participateEvent.getStatus(),
-        participateEvent.getServiceName(), participateEvent.getInstanceId());
+      LOG.info("Registered participated event, global tx: {}, local tx: {}, parent id: {}, "
+              + "confirm: {}, cancel: {}, status: {}, service [{}] instanceId [{}]",
+          participatedEvent.getGlobalTxId(), participatedEvent.getLocalTxId(), participatedEvent.getParentTxId(),
+          participatedEvent.getConfirmMethod(), participatedEvent.getCancelMethod(), participatedEvent.getStatus(),
+          participatedEvent.getServiceName(), participatedEvent.getInstanceId());
+    }
   }
 
   /**