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 0395ee4b6abc5515e15b8984253807c2b651e45a Author: seanyinx <[email protected]> AuthorDate: Tue Dec 26 17:06:23 2017 +0800 SCB-97 glued omega & alpha together Signed-off-by: seanyinx <[email protected]> --- .../io/servicecomb/saga/alpha/core/TxEvent.java | 12 +- alpha/alpha-server/pom.xml | 32 +++++ .../alpha/server/SwiftTxEventEndpointImpl.java | 4 +- .../saga/alpha/server/TxEventEnvelope.java | 4 +- .../src/main/resources/application.yaml | 31 +++++ .../alpha-server/src/main/resources/log4j2.xml | 40 ++---- .../src/main/resources/schema-mysql.sql | 11 ++ integration-tests/coverage-aggregate/pom.xml | 8 ++ .../pack-tests}/pom.xml | 140 ++++++--------------- .../pack/tests/GreetingApplication.java | 15 ++- .../integration/pack/tests/GreetingController.java | 52 ++++++++ .../integration/pack/tests/GreetingService.java | 18 ++- .../saga/integration/pack/tests/PackIT.java | 52 ++++++++ integration-tests/pom.xml | 1 + .../connector/thrift/ThriftMessageSender.java | 21 +++- {integration-tests => omega/omega-format}/pom.xml | 43 ++++--- .../saga/omega/format/NativeMessageFormat.java | 53 ++++++++ .../saga/omega/format/NativeMessageFormatTest.java | 70 +++++++++++ omega/{ => omega-spring-starter}/pom.xml | 53 ++++---- .../saga/omega/spring/EnableOmega.java} | 17 ++- .../saga/omega/spring/OmegaSpringConfig.java | 52 ++++++++ .../spring/CompensableMethodCheckingCallback.java | 3 +- .../spring/TransactionAspectConfig.java | 2 +- .../spring/CompensableAnnotationCheckingTest.java | 2 +- .../omega/transaction/MessageDeserializer.java | 2 +- ...essageDeserializer.java => OmegaException.java} | 7 +- omega/pom.xml | 2 + pom.xml | 27 +++- .../conditional-transaction-demo-tests/pom.xml | 2 +- .../demo-tests/pom.xml | 2 +- 30 files changed, 571 insertions(+), 207 deletions(-) diff --git a/alpha/alpha-core/src/main/java/io/servicecomb/saga/alpha/core/TxEvent.java b/alpha/alpha-core/src/main/java/io/servicecomb/saga/alpha/core/TxEvent.java index abd8fbd..2d0a19b 100644 --- a/alpha/alpha-core/src/main/java/io/servicecomb/saga/alpha/core/TxEvent.java +++ b/alpha/alpha-core/src/main/java/io/servicecomb/saga/alpha/core/TxEvent.java @@ -17,8 +17,10 @@ package io.servicecomb.saga.alpha.core; +import java.util.Date; + public class TxEvent { - private long timestamp; + private Date creationTime; private String globalTxId; private String localTxId; private String parentTxId; @@ -28,8 +30,8 @@ public class TxEvent { private TxEvent() { } - public TxEvent(long timestamp, String globalTxId, String localTxId, String parentTxId, String type, byte[] payloads) { - this.timestamp = timestamp; + public TxEvent(Date creationTime, String globalTxId, String localTxId, String parentTxId, String type, byte[] payloads) { + this.creationTime = creationTime; this.globalTxId = globalTxId; this.localTxId = localTxId; this.parentTxId = parentTxId; @@ -37,8 +39,8 @@ public class TxEvent { this.payloads = payloads; } - public long timestamp() { - return timestamp; + public Date creationTime() { + return creationTime; } public String globalTxId() { diff --git a/alpha/alpha-server/pom.xml b/alpha/alpha-server/pom.xml index 78723d5..4bc347e 100644 --- a/alpha/alpha-server/pom.xml +++ b/alpha/alpha-server/pom.xml @@ -65,6 +65,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> @@ -87,6 +91,34 @@ <artifactId>swift-maven-plugin</artifactId> </plugin> --> + <!-- mixin plugin configurations declared in another pom, + just like importing dependencies managed in another pom --> + <plugin> + <groupId>com.github.odavid.maven.plugins</groupId> + <artifactId>mixin-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> </plugins> </build> + + <profiles> + <profile> + <id>docker</id> + <build> + <plugins> + <plugin> + <groupId>io.fabric8</groupId> + <artifactId>docker-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.commonjava.maven.plugins</groupId> + <artifactId>directory-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> \ No newline at end of file diff --git a/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/SwiftTxEventEndpointImpl.java b/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/SwiftTxEventEndpointImpl.java index 4fd7d87..3ae39f6 100644 --- a/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/SwiftTxEventEndpointImpl.java +++ b/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/SwiftTxEventEndpointImpl.java @@ -17,6 +17,8 @@ package io.servicecomb.saga.alpha.server; +import java.util.Date; + import io.servicecomb.saga.alpha.core.TxEvent; import io.servicecomb.saga.alpha.core.TxEventRepository; import io.servicecomb.saga.pack.contracts.thrift.SwiftTxEvent; @@ -33,7 +35,7 @@ class SwiftTxEventEndpointImpl implements SwiftTxEventEndpoint { @Override public void handle(SwiftTxEvent message) { eventRepository.save(new TxEvent( - message.timestamp(), + new Date(message.timestamp()), message.globalTxId(), message.localTxId(), message.parentTxId(), diff --git a/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/TxEventEnvelope.java b/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/TxEventEnvelope.java index d5428c4..adbb28e 100644 --- a/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/TxEventEnvelope.java +++ b/alpha/alpha-server/src/main/java/io/servicecomb/saga/alpha/server/TxEventEnvelope.java @@ -40,8 +40,8 @@ class TxEventEnvelope { this.event = event; } - public long timestamp() { - return event.timestamp(); + public long creationTime() { + return event.creationTime().getTime(); } String globalTxId() { diff --git a/alpha/alpha-server/src/main/resources/application.yaml b/alpha/alpha-server/src/main/resources/application.yaml new file mode 100644 index 0000000..9b166c8 --- /dev/null +++ b/alpha/alpha-server/src/main/resources/application.yaml @@ -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. +## --------------------------------------------------------------------------- +spring: + profiles: prd + datasource: + username: saga + password: password + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://mysql.servicecomb.io:3306/saga?useSSL=false + platform: mysql + continue-on-error: true + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL5Dialect + hibernate: + ddl-auto: none diff --git a/integration-tests/pom.xml b/alpha/alpha-server/src/main/resources/log4j2.xml similarity index 50% copy from integration-tests/pom.xml copy to alpha/alpha-server/src/main/resources/log4j2.xml index 8b71db2..cae04cb 100644 --- a/integration-tests/pom.xml +++ b/alpha/alpha-server/src/main/resources/log4j2.xml @@ -16,31 +16,15 @@ ~ 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>saga</artifactId> - <groupId>io.servicecomb.saga</groupId> - <version>0.0.3-SNAPSHOT</version> - </parent> - <modelVersion>4.0.0</modelVersion> - - <groupId>io.servicecomb.saga.tests</groupId> - <artifactId>integration-tests</artifactId> - <packaging>pom</packaging> - <modules> - <module>coverage-aggregate</module> - </modules> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> - </build> - -</project> +<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> + <AsyncRoot level="info"> + <AppenderRef ref="Console"/> + </AsyncRoot> + </Loggers> +</Configuration> diff --git a/alpha/alpha-server/src/main/resources/schema-mysql.sql b/alpha/alpha-server/src/main/resources/schema-mysql.sql new file mode 100644 index 0000000..c3175ed --- /dev/null +++ b/alpha/alpha-server/src/main/resources/schema-mysql.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS `tx_event_envelope` ( + `surrogate_id` bigint NOT NULL AUTO_INCREMENT, + `creation_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `global_tx_id` varchar(36) NOT NULL, + `local_tx_id` varchar(36) NOT NULL, + `parent_tx_id` varchar(36) DEFAULT NULL, + `type` varchar(50) NOT NULL, + `payloads` varbinary(10240), + PRIMARY KEY (`surrogate_id`), + INDEX `running_sagas_index` (`global_tx_id`, `local_tx_id`, `type`) +) DEFAULT CHARSET=utf8; diff --git a/integration-tests/coverage-aggregate/pom.xml b/integration-tests/coverage-aggregate/pom.xml index a32f598..452fc56 100644 --- a/integration-tests/coverage-aggregate/pom.xml +++ b/integration-tests/coverage-aggregate/pom.xml @@ -63,6 +63,14 @@ <groupId>io.servicecomb.saga</groupId> <artifactId>omega-transport-resttemplate</artifactId> </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-format</artifactId> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>alpha-server</artifactId> + </dependency> </dependencies> <profiles> diff --git a/saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml b/integration-tests/pack-tests/pom.xml similarity index 64% copy from saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml copy to integration-tests/pack-tests/pom.xml index f1a5bba..bc7717c 100644 --- a/saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml +++ b/integration-tests/pack-tests/pom.xml @@ -16,18 +16,36 @@ ~ 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"> +<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>conditional-transaction-demo</artifactId> - <groupId>io.servicecomb.saga.demo</groupId> + <artifactId>integration-tests</artifactId> + <groupId>io.servicecomb.saga.tests</groupId> <version>0.0.3-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> - <artifactId>conditional-transaction-demo-tests</artifactId> + <artifactId>pack-tests</artifactId> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring.boot.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> <dependencies> <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-spring-starter</artifactId> + </dependency> + <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> @@ -44,6 +62,16 @@ </dependency> <dependency> <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> @@ -86,107 +114,19 @@ </run> </image> <image> - <name>payment:${project.version}</name> - <alias>payment</alias> - <run> - <env> - <JAVA_OPTS /> - </env> - <wait> - <log>Tomcat started on port</log> - <tcp> - <ports> - <port>8080</port> - </ports> - </tcp> - <time>120000</time> - </wait> - <ports> - <port>payment.port:8080</port> - </ports> - </run> - </image> - <image> - <name>membership:${project.version}</name> - <alias>membership</alias> - <run> - <env> - <JAVA_OPTS /> - </env> - <wait> - <log>Tomcat started on port</log> - <tcp> - <ports> - <port>8080</port> - </ports> - </tcp> - <time>120000</time> - </wait> - <ports> - <port>membership.port:8080</port> - </ports> - </run> - </image> - <image> - <name>inventory:${project.version}</name> - <alias>inventory</alias> - <run> - <env> - <JAVA_OPTS /> - </env> - <wait> - <log>Tomcat started on port</log> - <tcp> - <ports> - <port>8080</port> - </ports> - </tcp> - <time>120000</time> - </wait> - <ports> - <port>inventory.port:8080</port> - </ports> - </run> - </image> - <image> - <name>supplier:${project.version}</name> - <alias>supplier</alias> - <run> - <env> - <JAVA_OPTS /> - </env> - <wait> - <log>Tomcat started on port</log> - <tcp> - <ports> - <port>8080</port> - </ports> - </tcp> - <time>120000</time> - </wait> - <ports> - <port>supplier.port:8080</port> - </ports> - </run> - </image> - <image> - <name>saga-spring:${project.version}</name> - <alias>saga</alias> + <name>alpha-server:${project.version}</name> + <alias>alpha</alias> <run> <env> <JAVA_OPTS> - -Dspring.profiles.active=prd -Dspring.main.webEnvironment=true + -Dspring.profiles.active=prd </JAVA_OPTS> </env> <links> <link>mysql:mysql.servicecomb.io</link> - <link>payment:payment.servicecomb.io</link> - <link>membership:membership.servicecomb.io</link> - <link>inventory:inventory.servicecomb.io</link> - <link>supplier:supplier.servicecomb.io</link> </links> <wait> - <log>Tomcat started on port</log> + <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log> <tcp> <ports> <port>8080</port> @@ -195,7 +135,7 @@ <time>120000</time> </wait> <ports> - <port>saga.port:8080</port> + <port>alpha.port:8080</port> </ports> <dependsOn> <dependsOn>mysql</dependsOn> @@ -250,11 +190,11 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>2.19.1</version> + <version>${maven.failsafe.version}</version> <configuration> <systemPropertyVariables> <saga.address> - http://${docker.hostname}:${saga.port} + http://${docker.hostname}:${alpha.port} </saga.address> </systemPropertyVariables> <argLine>${jacoco.failsafe.argLine}</argLine> @@ -302,4 +242,4 @@ </profile> </profiles> -</project> +</project> \ No newline at end of file diff --git a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingApplication.java similarity index 66% copy from omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java copy to integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingApplication.java index d4315b3..24bce7a 100644 --- a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java +++ b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingApplication.java @@ -15,8 +15,17 @@ * limitations under the License. */ -package io.servicecomb.saga.omega.transaction; +package io.servicecomb.saga.integration.pack.tests; -public interface MessageDeserializer { - <T> T deserialize(byte[] message); +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import io.servicecomb.saga.omega.spring.EnableOmega; + +@EnableOmega +@SpringBootApplication +public class GreetingApplication { + public static void main(String[] args) { + SpringApplication.run(GreetingApplication.class, args); + } } diff --git a/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingController.java b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingController.java new file mode 100644 index 0000000..11aa0e8 --- /dev/null +++ b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingController.java @@ -0,0 +1,52 @@ +/* + * 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 io.servicecomb.saga.integration.pack.tests; + +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import io.servicecomb.saga.omega.context.OmegaContext; + +@Controller +@RequestMapping("/") +public class GreetingController { + private final GreetingService greetingService; + private final OmegaContext context; + + @Autowired + public GreetingController(GreetingService greetingService, OmegaContext context) { + this.greetingService = greetingService; + this.context = context; + } + + + @GetMapping("/greet") + ResponseEntity<String> greet(@RequestParam String name) { + // TODO: 2017/12/26 to be removed when tx id retrieval is done + context.setGlobalTxId(UUID.randomUUID().toString()); + context.setLocalTxId(UUID.randomUUID().toString()); + + return ResponseEntity.ok(greetingService.greet(name)); + } +} diff --git a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingService.java similarity index 67% copy from omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java copy to integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingService.java index d4315b3..d65899b 100644 --- a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java +++ b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/GreetingService.java @@ -15,8 +15,20 @@ * limitations under the License. */ -package io.servicecomb.saga.omega.transaction; +package io.servicecomb.saga.integration.pack.tests; -public interface MessageDeserializer { - <T> T deserialize(byte[] message); +import org.springframework.stereotype.Service; + +import io.servicecomb.saga.omega.transaction.annotations.Compensable; + +@Service +public class GreetingService { + @Compensable(compensationMethod = "goodbye") + String greet(String name) { + return "Greetings, " + name; + } + + String goodbye(String name) { + return "Goodbye, " + name; + } } diff --git a/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/PackIT.java b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/PackIT.java new file mode 100644 index 0000000..ad6bed1 --- /dev/null +++ b/integration-tests/pack-tests/src/test/java/io/servicecomb/saga/integration/pack/tests/PackIT.java @@ -0,0 +1,52 @@ +/* + * 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 io.servicecomb.saga.integration.pack.tests; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.springframework.http.HttpStatus.OK; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import io.servicecomb.saga.omega.context.OmegaContext; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = GreetingApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"alpha.cluster.address=localhost:32782"}) +public class PackIT { + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private OmegaContext omegaContext; + + + @Test + public void updatesTxStateToAlpha() throws Exception { + ResponseEntity<String> entity = restTemplate.getForEntity("/greet?name={name}", String.class, "mike"); + + assertThat(entity.getStatusCode(), is(OK)); + assertThat(entity.getBody(), is("Greetings, mike")); + } +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 8b71db2..cf1cc99 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -29,6 +29,7 @@ <packaging>pom</packaging> <modules> <module>coverage-aggregate</module> + <module>pack-tests</module> </modules> <build> diff --git a/omega/omega-connector/omega-connector-thrift/src/main/java/io/servicecomb/saga/omega/connector/thrift/ThriftMessageSender.java b/omega/omega-connector/omega-connector-thrift/src/main/java/io/servicecomb/saga/omega/connector/thrift/ThriftMessageSender.java index ed44e0c..bbf9bfa 100644 --- a/omega/omega-connector/omega-connector-thrift/src/main/java/io/servicecomb/saga/omega/connector/thrift/ThriftMessageSender.java +++ b/omega/omega-connector/omega-connector-thrift/src/main/java/io/servicecomb/saga/omega/connector/thrift/ThriftMessageSender.java @@ -17,17 +17,34 @@ package io.servicecomb.saga.omega.connector.thrift; +import static com.google.common.net.HostAndPort.fromParts; + +import java.util.concurrent.ExecutionException; + +import com.facebook.nifty.client.FramedClientConnector; +import com.facebook.swift.service.ThriftClientManager; + import io.servicecomb.saga.omega.transaction.MessageSender; import io.servicecomb.saga.omega.transaction.MessageSerializer; import io.servicecomb.saga.omega.transaction.TxEvent; import io.servicecomb.saga.pack.contracts.thrift.SwiftTxEvent; import io.servicecomb.saga.pack.contracts.thrift.SwiftTxEventEndpoint; -class ThriftMessageSender implements MessageSender { - +public class ThriftMessageSender implements MessageSender { + private static final ThriftClientManager clientManager = new ThriftClientManager(); private final SwiftTxEventEndpoint eventService; private final MessageSerializer serializer; + public static ThriftMessageSender create(String host, int port, MessageSerializer serializer) { + FramedClientConnector connector = new FramedClientConnector(fromParts(host, port)); + try { + SwiftTxEventEndpoint endpoint = clientManager.createClient(connector, SwiftTxEventEndpoint.class).get(); + return new ThriftMessageSender(endpoint, serializer); + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException("Failed to create transaction event endpoint client to " + host + ":" + port, e); + } + } + ThriftMessageSender(SwiftTxEventEndpoint eventService, MessageSerializer serializer) { this.eventService = eventService; this.serializer = serializer; diff --git a/integration-tests/pom.xml b/omega/omega-format/pom.xml similarity index 56% copy from integration-tests/pom.xml copy to omega/omega-format/pom.xml index 8b71db2..e7d2dc9 100644 --- a/integration-tests/pom.xml +++ b/omega/omega-format/pom.xml @@ -16,31 +16,36 @@ ~ 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"> +<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>saga</artifactId> + <artifactId>omega</artifactId> <groupId>io.servicecomb.saga</groupId> <version>0.0.3-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> - <groupId>io.servicecomb.saga.tests</groupId> - <artifactId>integration-tests</artifactId> - <packaging>pom</packaging> - <modules> - <module>coverage-aggregate</module> - </modules> + <artifactId>omega-format</artifactId> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> - </build> + <dependencies> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-transaction</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-all</artifactId> + </dependency> + <dependency> + <groupId>com.github.seanyinx</groupId> + <artifactId>unit-scaffolding</artifactId> + </dependency> + </dependencies> </project> diff --git a/omega/omega-format/src/main/java/io/servicecomb/saga/omega/format/NativeMessageFormat.java b/omega/omega-format/src/main/java/io/servicecomb/saga/omega/format/NativeMessageFormat.java new file mode 100644 index 0000000..14b1d29 --- /dev/null +++ b/omega/omega-format/src/main/java/io/servicecomb/saga/omega/format/NativeMessageFormat.java @@ -0,0 +1,53 @@ +/* + * 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 io.servicecomb.saga.omega.format; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import io.servicecomb.saga.omega.transaction.MessageDeserializer; +import io.servicecomb.saga.omega.transaction.MessageSerializer; +import io.servicecomb.saga.omega.transaction.OmegaException; +import io.servicecomb.saga.omega.transaction.TxEvent; + +public class NativeMessageFormat implements MessageSerializer, MessageDeserializer { + @Override + public byte[] serialize(TxEvent event) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (ObjectOutputStream outputStream = new ObjectOutputStream(out)) { + outputStream.writeObject(event.payloads()); + return out.toByteArray(); + } + } catch (IOException e) { + throw new OmegaException("Unable to serialize event with global tx id " + event.globalTxId(), e); + } + } + + @Override + public Object[] deserialize(byte[] message) { + try (ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(message))) { + return (Object[]) inputStream.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new OmegaException("Unable to deserialize message", e); + } + } +} diff --git a/omega/omega-format/src/test/java/io/servicecomb/saga/omega/format/NativeMessageFormatTest.java b/omega/omega-format/src/test/java/io/servicecomb/saga/omega/format/NativeMessageFormatTest.java new file mode 100644 index 0000000..4c93270 --- /dev/null +++ b/omega/omega-format/src/test/java/io/servicecomb/saga/omega/format/NativeMessageFormatTest.java @@ -0,0 +1,70 @@ +/* + * 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 io.servicecomb.saga.omega.format; + +import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing; +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +import io.servicecomb.saga.omega.transaction.OmegaException; +import io.servicecomb.saga.omega.transaction.TxEvent; + +public class NativeMessageFormatTest { + + private final NativeMessageFormat format = new NativeMessageFormat(); + + @Test + public void serializeObjectIntoBytes() throws Exception { + byte[] bytes = format.serialize(eventOf("hello", "world")); + + Object[] message = format.deserialize(bytes); + + assertThat(asList(message), contains("hello", "world")); + } + + @Test + public void blowsUpWhenObjectIsNotSerializable() throws Exception { + try { + format.serialize(eventOf(new NotSerializable())); + expectFailing(OmegaException.class); + } catch (OmegaException e) { + assertThat(e.getMessage(), startsWith("Unable to serialize event with global tx id")); + } + } + + @Test + public void blowsUpWhenObjectIsNotDeserializable() throws Exception { + try { + format.deserialize(new byte[0]); + expectFailing(OmegaException.class); + } catch (OmegaException e) { + assertThat(e.getMessage(), startsWith("Unable to deserialize message")); + } + } + + private TxEvent eventOf(Object... payloads) { + return new TxEvent(null, null, null, payloads); + } + + private static class NotSerializable { + } +} \ No newline at end of file diff --git a/omega/pom.xml b/omega/omega-spring-starter/pom.xml similarity index 54% copy from omega/pom.xml copy to omega/omega-spring-starter/pom.xml index ac4e397..b900a47 100644 --- a/omega/pom.xml +++ b/omega/omega-spring-starter/pom.xml @@ -20,42 +20,31 @@ 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>saga</artifactId> + <artifactId>omega</artifactId> <groupId>io.servicecomb.saga</groupId> <version>0.0.3-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> - <artifactId>omega</artifactId> - <packaging>pom</packaging> - <modules> - <module>omega-transaction</module> - <module>omega-context</module> - <module>omega-spring-tx</module> - <module>omega-transport</module> - <module>omega-connector</module> - </modules> + <artifactId>omega-spring-starter</artifactId> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-dependencies</artifactId> - <version>${spring.boot.version}</version> - <type>pom</type> - <scope>import</scope> - </dependency> - <dependency> - <groupId>io.servicecomb.saga</groupId> - <artifactId>omega-context</artifactId> - <version>0.0.3-SNAPSHOT</version> - </dependency> - <dependency> - <groupId>io.servicecomb.saga</groupId> - <artifactId>omega-transaction</artifactId> - <version>0.0.3-SNAPSHOT</version> - </dependency> - </dependencies> - </dependencyManagement> + <dependencies> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-transport-resttemplate</artifactId> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-spring-tx</artifactId> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-connector-thrift</artifactId> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-format</artifactId> + </dependency> + </dependencies> -</project> \ No newline at end of file +</project> diff --git a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java b/omega/omega-spring-starter/src/main/java/io/servicecomb/saga/omega/spring/EnableOmega.java similarity index 61% copy from omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java copy to omega/omega-spring-starter/src/main/java/io/servicecomb/saga/omega/spring/EnableOmega.java index d4315b3..8d5f851 100644 --- a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java +++ b/omega/omega-spring-starter/src/main/java/io/servicecomb/saga/omega/spring/EnableOmega.java @@ -15,8 +15,19 @@ * limitations under the License. */ -package io.servicecomb.saga.omega.transaction; +package io.servicecomb.saga.omega.spring; -public interface MessageDeserializer { - <T> T deserialize(byte[] message); +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +import io.servicecomb.saga.omega.transaction.spring.TransactionAspectConfig; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import({OmegaSpringConfig.class, TransactionAspectConfig.class}) +public @interface EnableOmega { } diff --git a/omega/omega-spring-starter/src/main/java/io/servicecomb/saga/omega/spring/OmegaSpringConfig.java b/omega/omega-spring-starter/src/main/java/io/servicecomb/saga/omega/spring/OmegaSpringConfig.java new file mode 100644 index 0000000..ae92123 --- /dev/null +++ b/omega/omega-spring-starter/src/main/java/io/servicecomb/saga/omega/spring/OmegaSpringConfig.java @@ -0,0 +1,52 @@ +/* + * 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 io.servicecomb.saga.omega.spring; + +import java.lang.invoke.MethodHandles; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.servicecomb.saga.omega.connector.thrift.ThriftMessageSender; +import io.servicecomb.saga.omega.format.NativeMessageFormat; +import io.servicecomb.saga.omega.transaction.MessageSender; + +@Configuration +class OmegaSpringConfig { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Bean + MessageSender messageSender(@Value("${alpha.cluster.address}") String[] addresses) { + // TODO: 2017/12/26 connect to the one with lowest latency + for (String address : addresses) { + try { + String[] pair = address.split(":"); + return ThriftMessageSender.create(pair[0], Integer.parseInt(pair[1]), new NativeMessageFormat()); + } catch (Exception e) { + log.error("Unable to connect to alpha at {}", address, e); + } + } + + throw new IllegalArgumentException( + "None of the alpha cluster is reachable: " + Arrays.toString(addresses)); + } +} diff --git a/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/CompensableMethodCheckingCallback.java b/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/CompensableMethodCheckingCallback.java index c29061c..23352f4 100644 --- a/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/CompensableMethodCheckingCallback.java +++ b/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/CompensableMethodCheckingCallback.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils.MethodCallback; +import io.servicecomb.saga.omega.transaction.OmegaException; import io.servicecomb.saga.omega.transaction.annotations.Compensable; class CompensableMethodCheckingCallback implements MethodCallback { @@ -47,7 +48,7 @@ class CompensableMethodCheckingCallback implements MethodCallback { bean.getClass().getDeclaredMethod(compensationMethod, method.getParameterTypes()); LOG.debug("Found compensation method [{}] in {}", compensationMethod, bean.getClass().getCanonicalName()); } catch (NoSuchMethodException e) { - throw new IllegalArgumentException( + throw new OmegaException( "No such compensation method [" + compensationMethod + "] found in " + bean.getClass().getCanonicalName(), e); } diff --git a/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/TransactionAspectConfig.java b/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/TransactionAspectConfig.java index f44e66f..59f7d2e 100644 --- a/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/TransactionAspectConfig.java +++ b/omega/omega-spring-tx/src/main/java/io/servicecomb/saga/omega/transaction/spring/TransactionAspectConfig.java @@ -27,7 +27,7 @@ import io.servicecomb.saga.omega.transaction.TransactionAspect; @Configuration @EnableAspectJAutoProxy -class TransactionAspectConfig { +public class TransactionAspectConfig { @Bean OmegaContext omegaContext() { return new OmegaContext(); diff --git a/omega/omega-spring-tx/src/test/java/io/servicecomb/saga/omega/transaction/spring/CompensableAnnotationCheckingTest.java b/omega/omega-spring-tx/src/test/java/io/servicecomb/saga/omega/transaction/spring/CompensableAnnotationCheckingTest.java index e83a621..29698a0 100644 --- a/omega/omega-spring-tx/src/test/java/io/servicecomb/saga/omega/transaction/spring/CompensableAnnotationCheckingTest.java +++ b/omega/omega-spring-tx/src/test/java/io/servicecomb/saga/omega/transaction/spring/CompensableAnnotationCheckingTest.java @@ -33,7 +33,7 @@ public class CompensableAnnotationCheckingTest { try (ConfigurableApplicationContext ignored = new SpringApplicationBuilder(TransactionTestMain.class) .profiles("annotation-checking") .run()) { - expectFailing(IllegalArgumentException.class); + expectFailing(BeanCreationException.class); } } catch (BeanCreationException e) { assertThat(e.getCause().getMessage(), startsWith("No such compensation method [none]")); diff --git a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java b/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java index d4315b3..e5348f4 100644 --- a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java +++ b/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java @@ -18,5 +18,5 @@ package io.servicecomb.saga.omega.transaction; public interface MessageDeserializer { - <T> T deserialize(byte[] message); + Object[] deserialize(byte[] message); } diff --git a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java b/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/OmegaException.java similarity index 85% copy from omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java copy to omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/OmegaException.java index d4315b3..a67b2ce 100644 --- a/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/MessageDeserializer.java +++ b/omega/omega-transaction/src/main/java/io/servicecomb/saga/omega/transaction/OmegaException.java @@ -17,6 +17,9 @@ package io.servicecomb.saga.omega.transaction; -public interface MessageDeserializer { - <T> T deserialize(byte[] message); +public class OmegaException extends RuntimeException { + + public OmegaException(String cause, Throwable throwable) { + super(cause, throwable); + } } diff --git a/omega/pom.xml b/omega/pom.xml index ac4e397..a0def30 100644 --- a/omega/pom.xml +++ b/omega/pom.xml @@ -34,6 +34,8 @@ <module>omega-spring-tx</module> <module>omega-transport</module> <module>omega-connector</module> + <module>omega-spring-starter</module> + <module>omega-format</module> </modules> <dependencyManagement> diff --git a/pom.xml b/pom.xml index 026d3df..6668b59 100755 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,6 @@ <modules> <module>saga-core</module> - <module>integration-tests</module> <module>transports</module> <module>saga-spring</module> <module>docker-build-config</module> @@ -36,6 +35,7 @@ <module>omega</module> <module>alpha</module> <module>pack-contracts</module> + <module>integration-tests</module> </modules> <properties> @@ -50,6 +50,7 @@ <spring.version>4.3.7.RELEASE</spring.version> <akka.version>2.5.6</akka.version> <rat.version>0.12</rat.version> + <maven.failsafe.version>2.19.1</maven.failsafe.version> </properties> <name>ServiceComb Saga</name> @@ -152,11 +153,31 @@ </dependency> <dependency> <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-connector-thrift</artifactId> + <version>0.0.3-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-spring-starter</artifactId> + <version>0.0.3-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> + <artifactId>omega-format</artifactId> + <version>0.0.3-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> <artifactId>alpha-core</artifactId> <version>0.0.3-SNAPSHOT</version> </dependency> <dependency> <groupId>io.servicecomb.saga</groupId> + <artifactId>alpha-server</artifactId> + <version>0.0.3-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>io.servicecomb.saga</groupId> <artifactId>pack-contract-thrift</artifactId> <version>0.0.3-SNAPSHOT</version> </dependency> @@ -294,6 +315,10 @@ <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> </exclusion> + <exclusion> + <groupId>org.apache.bval</groupId> + <artifactId>bval-jsr303</artifactId> + </exclusion> </exclusions> </dependency> diff --git a/saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml b/saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml index f1a5bba..a037c14 100644 --- a/saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml +++ b/saga-demo/conditional-transaction-demo/conditional-transaction-demo-tests/pom.xml @@ -250,7 +250,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>2.19.1</version> + <version>${maven.failsafe.version}</version> <configuration> <systemPropertyVariables> <saga.address> diff --git a/saga-demo/dependency-free-transaction-demo/demo-tests/pom.xml b/saga-demo/dependency-free-transaction-demo/demo-tests/pom.xml index 6684628..80568fd 100644 --- a/saga-demo/dependency-free-transaction-demo/demo-tests/pom.xml +++ b/saga-demo/dependency-free-transaction-demo/demo-tests/pom.xml @@ -282,7 +282,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>2.19.1</version> + <version>${maven.failsafe.version}</version> <configuration> <systemPropertyVariables> <saga.address> -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
