This is an automated email from the ASF dual-hosted git repository. zhanglei pushed a commit to branch SCB-1321 in repository https://gitbox.apache.org/repos/asf/servicecomb-pack.git
commit e1024e0fde2792419824a368e8030e6f8a8afa4a Author: Lei Zhang <[email protected]> AuthorDate: Mon Jul 8 20:16:12 2019 +0800 SCB-1321 Add acceptance-pack-akka-spring --- .../acceptance-pack-akka-spring-demo/pom.xml | 380 +++++++++++++++++++++ .../org/apache/servicecomb/pack/PackStepdefs.java | 211 ++++++++++++ .../org/apache/servicecomb/pack/RunCucumberIT.java | 29 ++ .../resources/booking_exception_after_post_car.btm | 28 ++ .../booking_exception_after_post_hotel.btm | 28 ++ .../src/test/resources/booking_timeout.btm | 36 ++ .../src/test/resources/log4j2-test.xml | 30 ++ ...ion_after_post_car_compensated_scenario.feature | 42 +++ ...n_after_post_hotel_compensated_scenario.feature | 46 +++ ...pack_booking_timeout_suspended_scenario.feature | 44 +++ ...ck_booking_timeout_suspended_scenario.feature 2 | 44 +++ .../pack_car_fail_compensated_scenario.feature | 37 ++ .../pack_hotel_fail_compensated_scenario.feature | 41 +++ .../test/resources/pack_success_scenario.feature | 41 +++ acceptance-tests/pom.xml | 3 +- .../pack/alpha/server/AlphaEventController.java | 2 + .../FsmSagaDataController.java} | 47 +-- 17 files changed, 1068 insertions(+), 21 deletions(-) diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/pom.xml b/acceptance-tests/acceptance-pack-akka-spring-demo/pom.xml new file mode 100644 index 0000000..c15d5d4 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/pom.xml @@ -0,0 +1,380 @@ +<?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.pack</groupId> + <version>0.5.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>acceptance-pack-akka-spring</artifactId> + <name>Pack:Acceptance Tests::Akka 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> + <dependency> + <groupId>org.jboss.byteman</groupId> + <artifactId>byteman-submit</artifactId> + <version>${byteman.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,test + </JAVA_OPTS> + <alpha.feature.akka.enabled>true</alpha.feature.akka.enabled> + </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>car:${project.version}</name> + <alias>car</alias> + <run> + <env> + <JAVA_OPTS> + -Dorg.jboss.byteman.debug=true -Dorg.jboss.byteman.verbose=true + -javaagent:/maven/saga/byteman.jar=port:9091,address:0.0.0.0,listener:true + </JAVA_OPTS> + <alpha.feature.akka.enabled>true</alpha.feature.akka.enabled> + </env> + <wait> + <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log> + <tcp> + <ports> + <port>8080</port> + <port>9091</port> + </ports> + </tcp> + <time>120000</time> + </wait> + <links> + <link>alpha:alpha-server.servicecomb.io</link> + </links> + <ports> + <port>car.port:8080</port> + <port>car.byteman.port:9091</port> + </ports> + <dependsOn> + <container>alpha</container> + </dependsOn> + </run> + </image> + <image> + <name>hotel:${project.version}</name> + <alias>hotel</alias> + <run> + <env> + <JAVA_OPTS> + -Dorg.jboss.byteman.debug=true -Dorg.jboss.byteman.verbose=true + -javaagent:/maven/saga/byteman.jar=port:9091,address:0.0.0.0,listener:true + </JAVA_OPTS> + <alpha.feature.akka.enabled>true</alpha.feature.akka.enabled> + </env> + <wait> + <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log> + <tcp> + <ports> + <port>8080</port> + <port>9091</port> + </ports> + </tcp> + <time>120000</time> + </wait> + <links> + <link>alpha:alpha-server.servicecomb.io</link> + </links> + <ports> + <port>hotel.port:8080</port> + <port>hotel.byteman.port:9091</port> + </ports> + <dependsOn> + <container>alpha</container> + </dependsOn> + </run> + </image> + <image> + <name>booking:${project.version}</name> + <alias>booking</alias> + <run> + <env> + <JAVA_OPTS> + -Dorg.jboss.byteman.debug=true -Dorg.jboss.byteman.verbose=true + -javaagent:/maven/saga/byteman.jar=port:9091,address:0.0.0.0,listener:true + </JAVA_OPTS> + <alpha.feature.akka.enabled>true</alpha.feature.akka.enabled> + </env> + <wait> + <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log> + <tcp> + <ports> + <port>8080</port> + <port>9091</port> + </ports> + </tcp> + <time>120000</time> + </wait> + <links> + <link>alpha:alpha-server.servicecomb.io</link> + <link>car:car.servicecomb.io</link> + <link>hotel:hotel.servicecomb.io</link> + </links> + <ports> + <port>booking.port:8080</port> + <port>booking.byteman.port:9091</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> + <car.service.address> + http://${docker.hostname}:${car.port} + </car.service.address> + <hotel.service.address> + http://${docker.hostname}:${hotel.port} + </hotel.service.address> + <booking.service.address> + http://${docker.hostname}:${booking.port} + </booking.service.address> + <spring.datasource.url> + jdbc:postgresql://${docker.hostname}:${postgres.port}/saga?useSSL=false + </spring.datasource.url> + <byteman.address> + ${docker.hostname} + </byteman.address> + <car.byteman.port> + ${car.byteman.port} + </car.byteman.port> + <hotel.byteman.port> + ${hotel.byteman.port} + </hotel.byteman.port> + <booking.byteman.port> + ${booking.byteman.port} + </booking.byteman.port> + <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> + </profiles> + +</project> diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/java/org/apache/servicecomb/pack/PackStepdefs.java b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/java/org/apache/servicecomb/pack/PackStepdefs.java new file mode 100644 index 0000000..711edd2 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/java/org/apache/servicecomb/pack/PackStepdefs.java @@ -0,0 +1,211 @@ +/* + * 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.pack; + +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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.jboss.byteman.agent.submit.Submit; +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 CAR_SERVICE_ADDRESS = "car.service.address"; + private static final String HOTEL_SERVICE_ADDRESS = "hotel.service.address"; + private static final String BOOKING_SERVICE_ADDRESS = "booking.service.address"; + private static final String INFO_SERVICE_URI = "info.service.uri"; + private static final String[] addresses = {CAR_SERVICE_ADDRESS, HOTEL_SERVICE_ADDRESS}; + + private static final Consumer<Map<String, String>[]> NO_OP_CONSUMER = (dataMap) -> { + }; + + private static final Map<String, Submit> submits = new HashMap<>(); + + public PackStepdefs() { + Given("^Car Service is up and running$", () -> { + probe(System.getProperty(CAR_SERVICE_ADDRESS)); + }); + + And("^Hotel Service is up and running$", () -> { + probe(System.getProperty(HOTEL_SERVICE_ADDRESS)); + }); + + And("^Booking Service is up and running$", () -> { + probe(System.getProperty(BOOKING_SERVICE_ADDRESS)); + }); + + And("^Alpha is up and running$", () -> { + probe(System.getProperty(ALPHA_REST_ADDRESS)); + }); + + Given("^Install the byteman script ([A-Za-z0-9_\\.]+) to ([A-Za-z]+) Service$", (String script, String service) -> { + LOG.info("Install the byteman script {} to {} service", script, service); + List<String> rules = new ArrayList<>(); + rules.add("target/test-classes/" + script); + Submit bm = getBytemanSubmit(service); + bm.addRulesFromFiles(rules); + }); + + When("^User ([A-Za-z]+) requests to book ([0-9]+) cars and ([0-9]+) rooms (success|fail)$", (username, cars, rooms, result) -> { + LOG.info("Received request from user {} to book {} cars and {} rooms", username, cars, rooms); + + Response resp = given() + .pathParam("name", username) + .pathParam("rooms", rooms) + .pathParam("cars", cars) + .when() + .post(System.getProperty("booking.service.address") + "/booking/{name}/{rooms}/{cars}"); + if (result.equals("success")) { + resp.then().statusCode(is(200)); + } else if (result.equals("fail")) { + resp.then().statusCode(is(500)); + } + }); + + 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) + "/saga/events", dataTable, columnStrippingConsumer); + }); + + And("^Car Service contains the following booking orders$", (DataTable dataTable) -> { + dataMatches(System.getProperty(CAR_SERVICE_ADDRESS) + "/bookings", dataTable, NO_OP_CONSUMER); + }); + + And("^Hotel Service contains the following booking orders$", (DataTable dataTable) -> { + dataMatches(System.getProperty(HOTEL_SERVICE_ADDRESS) + "/bookings", dataTable, NO_OP_CONSUMER); + }); + } + + @After + public void cleanUp() { + LOG.info("Cleaning up services"); + for (String address : addresses) { + given() + .when() + .delete(System.getProperty(address) + "/bookings") + .then() + .statusCode(is(200)); + } + + given() + .when() + .delete(System.getProperty(ALPHA_REST_ADDRESS) + "/saga/events") + .then() + .statusCode(is(200)); + + for (Submit bm : submits.values()) { + try { + bm.deleteAllRules(); + } catch (Exception e) { + LOG.warn("Fail to delete the byteman rules " + e); + } + } + } + + @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)); + } + + private Submit getBytemanSubmit(String service) { + if (submits.containsKey(service)) { + return submits.get(service); + } else { + String address = System.getProperty("byteman.address"); + String port = System.getProperty(service.toLowerCase() + ".byteman.port"); + Submit bm = new Submit(address, Integer.parseInt(port)); + submits.put(service, bm); + return bm; + } + } +} diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/java/org/apache/servicecomb/pack/RunCucumberIT.java b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/java/org/apache/servicecomb/pack/RunCucumberIT.java new file mode 100644 index 0000000..c8415ce --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/java/org/apache/servicecomb/pack/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.pack; + +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-akka-spring-demo/src/test/resources/booking_exception_after_post_car.btm b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_exception_after_post_car.btm new file mode 100644 index 0000000..5afea45 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_exception_after_post_car.btm @@ -0,0 +1,28 @@ +# 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. + +############################################################## +# rules to timeout the booking after invoking the services +# +############################################################### + +RULE throw exception +CLASS org.apache.servicecomb.pack.demo.booking.BookingController +METHOD postCarBooking +AT ENTRY +IF TRUE +DO debug("throw RuntimeException here"), + throw new RuntimeException("Cannot access the booking hotel server!") +ENDRULE diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_exception_after_post_hotel.btm b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_exception_after_post_hotel.btm new file mode 100644 index 0000000..d7c8a3f --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_exception_after_post_hotel.btm @@ -0,0 +1,28 @@ +# 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. + +############################################################## +# rules to timeout the booking after invoking the services +# +############################################################### + +RULE throw exception +CLASS org.apache.servicecomb.pack.demo.booking.BookingController +METHOD postBooking +AT ENTRY +IF TRUE +DO debug("throw RuntimeException here"), + throw new RuntimeException("Cannot access the booking hotel server!") +ENDRULE diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_timeout.btm b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_timeout.btm new file mode 100644 index 0000000..829e7ba --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/booking_timeout.btm @@ -0,0 +1,36 @@ +# 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. + +############################################################## +# rules to timeout the booking after invoking the services +# +############################################################### + +RULE set the saga timeout to 5s +INTERFACE org.apache.servicecomb.pack.omega.context.annotations.SagaStart +METHOD timeout +AT EXIT +IF TRUE +DO RETURN 5 +ENDRULE + +RULE sleep when postBooking until timeout happens +CLASS org.apache.servicecomb.pack.demo.booking.BookingController +METHOD postBooking +AT ENTRY +IF TRUE +DO debug("delay 10s until the booking timeout"), + Thread.sleep(10000) +ENDRULE diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/log4j2-test.xml b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/log4j2-test.xml new file mode 100644 index 0000000..58924c6 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-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-akka-spring-demo/src/test/resources/pack_booking_exception_after_post_car_compensated_scenario.feature b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_exception_after_post_car_compensated_scenario.feature new file mode 100644 index 0000000..1aae5ee --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_exception_after_post_car_compensated_scenario.feature @@ -0,0 +1,42 @@ +# 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: Booking Exception After Post Hotel Secenario + Given Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + Given Install the byteman script booking_exception_after_post_car.btm to Booking Service + + When User Sean requests to book 1 cars and 1 rooms fail + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxEndedEvent | + | booking | SagaAbortedEvent | + | car | TxCompensatedEvent | + + Then Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | false | true | + + Then Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_exception_after_post_hotel_compensated_scenario.feature b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_exception_after_post_hotel_compensated_scenario.feature new file mode 100644 index 0000000..aa7f155 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_exception_after_post_hotel_compensated_scenario.feature @@ -0,0 +1,46 @@ +# 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: Booking Exception After Post Car Secenario + Given Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + Given Install the byteman script booking_exception_after_post_hotel.btm to Booking Service + + When User Sean requests to book 1 cars and 1 rooms fail + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxEndedEvent | + | hotel | TxStartedEvent | + | hotel | TxEndedEvent | + | booking | SagaAbortedEvent | + | hotel | TxCompensatedEvent | + | car | TxCompensatedEvent | + + Then Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | false | true | + + Then Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | false | true | + diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_timeout_suspended_scenario.feature b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_timeout_suspended_scenario.feature new file mode 100644 index 0000000..1cfce0e --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_timeout_suspended_scenario.feature @@ -0,0 +1,44 @@ +# 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 transaction timeout and will be compensated + Given Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + Given Install the byteman script booking_timeout.btm to Booking Service + + When User Sean requests to book 1 cars and 1 rooms fail + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxEndedEvent | + | hotel | TxStartedEvent | + | hotel | TxEndedEvent | + + + Then Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | true | false | + + Then Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | true | false | + diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_timeout_suspended_scenario.feature 2 b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_timeout_suspended_scenario.feature 2 new file mode 100644 index 0000000..1cfce0e --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_booking_timeout_suspended_scenario.feature 2 @@ -0,0 +1,44 @@ +# 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 transaction timeout and will be compensated + Given Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + Given Install the byteman script booking_timeout.btm to Booking Service + + When User Sean requests to book 1 cars and 1 rooms fail + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxEndedEvent | + | hotel | TxStartedEvent | + | hotel | TxEndedEvent | + + + Then Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | true | false | + + Then Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | true | false | + diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_car_fail_compensated_scenario.feature b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_car_fail_compensated_scenario.feature new file mode 100644 index 0000000..01a073e --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_car_fail_compensated_scenario.feature @@ -0,0 +1,37 @@ +# 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 Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + When User Sean requests to book 11 cars and 1 rooms fail + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxAbortedEvent | + | booking | SagaAbortedEvent | + + Then Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + + Then Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_hotel_fail_compensated_scenario.feature b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_hotel_fail_compensated_scenario.feature new file mode 100644 index 0000000..f8f0e64 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_hotel_fail_compensated_scenario.feature @@ -0,0 +1,41 @@ +# 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 Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + When User Sean requests to book 5 cars and 3 rooms fail + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxEndedEvent | + | hotel | TxStartedEvent | + | hotel | TxAbortedEvent | + | booking | SagaAbortedEvent | + | car | TxCompensatedEvent | + + Then Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 5 | false | true | + + Then Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | diff --git a/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_success_scenario.feature b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_success_scenario.feature new file mode 100644 index 0000000..4157753 --- /dev/null +++ b/acceptance-tests/acceptance-pack-akka-spring-demo/src/test/resources/pack_success_scenario.feature @@ -0,0 +1,41 @@ +# 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: Success Scenario + Given Car Service is up and running + And Hotel Service is up and running + And Booking Service is up and running + And Alpha is up and running + + When User Sean requests to book 2 cars and 1 rooms success + + Then Alpha records the following events + | serviceName | type | + | booking | SagaStartedEvent | + | car | TxStartedEvent | + | car | TxEndedEvent | + | hotel | TxStartedEvent | + | hotel | TxEndedEvent | + | booking | SagaEndedEvent | + + And Car Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 2 | true | false | + + And Hotel Service contains the following booking orders + | id | name | amount | confirmed | cancelled | + | 1 | Sean | 1 | true | false | diff --git a/acceptance-tests/pom.xml b/acceptance-tests/pom.xml index 14098ac..13cf28f 100644 --- a/acceptance-tests/pom.xml +++ b/acceptance-tests/pom.xml @@ -35,7 +35,8 @@ <module>acceptance-pack-tcc-spring-demo</module> <module>acceptance-pack-cluster-spring-demo</module> <module>acceptance-pack-spring-demo-with-consul</module> - <module>acceptance-pack-spring-demo-with-zookeeper</module> +<!-- <module>acceptance-pack-spring-demo-with-zookeeper</module>--> + <module>acceptance-pack-akka-spring-demo</module> </modules> <properties> diff --git a/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java b/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java index 0303ead..2e6b8e0 100644 --- a/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java +++ b/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java @@ -25,6 +25,7 @@ import java.util.List; import org.apache.servicecomb.pack.alpha.core.TxEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -42,6 +43,7 @@ import kamon.annotation.Trace; @Controller @RequestMapping("/saga") @Profile("test") +@ConditionalOnProperty(name = "alpha.feature.akka.enabled", havingValue = "false", matchIfMissing = true) // Only export this Controller for test class AlphaEventController { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); diff --git a/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java b/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/fsm/FsmSagaDataController.java similarity index 67% copy from alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java copy to alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/fsm/FsmSagaDataController.java index 0303ead..7790286 100644 --- a/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/AlphaEventController.java +++ b/alpha/alpha-server/src/main/java/org/apache/servicecomb/pack/alpha/server/fsm/FsmSagaDataController.java @@ -15,16 +15,26 @@ * limitations under the License. */ -package org.apache.servicecomb.pack.alpha.server; +package org.apache.servicecomb.pack.alpha.server.fsm; +import akka.actor.ActorSystem; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import java.lang.invoke.MethodHandles; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; - +import java.util.Map; +import kamon.annotation.EnableKamon; +import kamon.annotation.Trace; import org.apache.servicecomb.pack.alpha.core.TxEvent; +import org.apache.servicecomb.pack.alpha.fsm.model.SagaData; +import org.apache.servicecomb.pack.alpha.fsm.spring.integration.akka.SagaDataExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -32,34 +42,31 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; - -import kamon.annotation.EnableKamon; -import kamon.annotation.Trace; - @EnableKamon @Controller @RequestMapping("/saga") @Profile("test") +@ConditionalOnProperty(name= "alpha.feature.akka.enabled", havingValue = "true") // Only export this Controller for test -class AlphaEventController { +class FsmSagaDataController { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final TxEventEnvelopeRepository eventRepository; - - AlphaEventController(TxEventEnvelopeRepository eventRepository) { - this.eventRepository = eventRepository; - } + @Autowired + ActorSystem system; @Trace("getEvents") @GetMapping(value = "/events") - ResponseEntity<Collection<TxEventVo>> events() { + ResponseEntity<Collection<Map>> events() { LOG.info("Get the events request"); - Iterable<TxEvent> events = eventRepository.findAll(); - - List<TxEventVo> eventVos = new LinkedList<>(); - events.forEach(event -> eventVos.add(new TxEventVo(event))); + List<Map> eventVos = new LinkedList<>(); + SagaData data = SagaDataExtension.SAGA_DATA_EXTENSION_PROVIDER.get(system).getLastSagaData(); + data.getEvents().forEach(event -> { + LOG.info(event.toString()); + Map<String,String> obj = new HashMap(); + obj.put("serviceName",event.getServiceName()); + obj.put("type",event.getClass().getSimpleName()); + eventVos.add(obj); + }); LOG.info("Get the event size " + eventVos.size()); return ResponseEntity.ok(eventVos); @@ -68,7 +75,7 @@ class AlphaEventController { @Trace("deleteEvents") @DeleteMapping("/events") ResponseEntity<String> clear() { - eventRepository.deleteAll(); + SagaDataExtension.SAGA_DATA_EXTENSION_PROVIDER.get(system).clearSagaData(); return ResponseEntity.ok("All events deleted"); }
