chapters;
+ private int pos;
+ private boolean forward;
+ private boolean finished;
+
+
+ public static Saga create() {
+ return new Saga();
+ }
+
+ /**
+ * get resuzlt of saga.
+ *
+ * @return result of saga @see {@link SagaResult}
+ */
+ public SagaResult getResult() {
+ if (finished) {
+ return forward
+ ? SagaResult.FINISHED
+ : SagaResult.ROLLBACKED;
+ }
+
+ return SagaResult.PROGRESS;
+ }
+
+ /**
+ * add chapter to saga.
+ * @param name chapter name
+ * @return this
+ */
+ public Saga chapter(String name) {
+ this.chapters.add(new Chapter(name));
+ return this;
+ }
+
+ /**
+ * set value to last chapter.
+ * @param value invalue
+ * @return this
+ */
+ public Saga setInValue(Object value) {
+ if (chapters.isEmpty()) {
+ return this;
+ }
+ chapters.get(chapters.size() - 1).setInValue(value);
+ return this;
+ }
+
+ /**
+ * get value from current chapter.
+ * @return value
+ */
+ public Object getCurrentValue() {
+ return chapters.get(pos).getInValue();
+ }
+
+ /**
+ * set value to current chapter.
+ * @param value to set
+ */
+ public void setCurrentValue(Object value) {
+ chapters.get(pos).setInValue(value);
+ }
+
+ /**
+ * set status for current chapter.
+ * @param result to set
+ */
+ public void setCurrentStatus(ChapterResult result) {
+ chapters.get(pos).setResult(result);
+ }
+
+ void setFinished(boolean finished) {
+ this.finished = finished;
+ }
+
+ boolean isForward() {
+ return forward;
+ }
+
+ int forward() {
+ return ++pos;
+ }
+
+ int back() {
+ this.forward = false;
+ return --pos;
+ }
+
+
+ private Saga() {
+ this.chapters = new ArrayList<>();
+ this.pos = 0;
+ this.forward = true;
+ this.finished = false;
+ }
+
+ Chapter getCurrent() {
+ return chapters.get(pos);
+ }
+
+
+ boolean isPresent() {
+ return pos >= 0 && pos < chapters.size();
+ }
+
+ boolean isCurrentSuccess() {
+ return chapters.get(pos).isSuccess();
+ }
+
+ /**
+ * Class presents a chapter status and incoming
+ * parameters(incoming parameter transforms to outcoming parameter).
+ */
+ public static class Chapter {
+ private String name;
+ private ChapterResult result;
+ private Object inValue;
+
+
+ public Chapter(String name) {
+ this.name = name;
+ this.result = ChapterResult.INIT;
+ }
+
+ public Object getInValue() {
+ return inValue;
+ }
+
+ public void setInValue(Object object) {
+ this.inValue = object;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * set result.
+ * @param result {@link ChapterResult}
+ */
+ public void setResult(ChapterResult result) {
+ this.result = result;
+ }
+
+ /**
+ * the result for chapter is good.
+ * @return true if is good otherwise bad
+ */
+ public boolean isSuccess() {
+ return result == ChapterResult.SUCCESS;
+ }
+ }
+
+
+ /**
+ * result for chapter.
+ */
+ public enum ChapterResult {
+ INIT, SUCCESS, ROLLBACK
+ }
+
+ /**
+ * result for saga.
+ */
+ public enum SagaResult {
+ PROGRESS, FINISHED, ROLLBACKED
+ }
+
+ @Override
+ public String toString() {
+ return "Saga{"
+ + "chapters="
+ + Arrays.toString(chapters.toArray())
+ + ", pos="
+ + pos
+ + ", forward="
+ + forward
+ + '}';
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java
new file mode 100644
index 000000000..d8844c864
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java
@@ -0,0 +1,84 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.choreography;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This pattern is used in distributed services to perform a group of operations atomically.
+ * This is an analog of transaction in a database but in terms
+ * of microservices architecture this is executed
+ * in a distributed environment
+ *
+ * A saga is a sequence of local transactions in a certain context.
+ * If one transaction fails for some reason,
+ * the saga executes compensating transactions(rollbacks)
+ * to undo the impact of the preceding transactions.
+ *
+ *
In this approach, there are no mediators or orchestrators services.
+ * All chapters are handled and moved by services manually.
+ *
+ *
The major difference with choreography saga is an ability to handle crashed services
+ * (otherwise in choreography services very hard to prevent a saga
+ * if one of them has been crashed)
+ *
+ * @see com.iluwatar.saga.choreography.Saga
+ * @see Service
+ */
+public class SagaApplication {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class);
+
+ /**
+ * main method.
+ */
+ public static void main(String[] args) {
+ ServiceDiscoveryService sd = serviceDiscovery();
+ ChoreographyChapter service = sd.findAny();
+ Saga goodOrderSaga = service.execute(newSaga("good_order"));
+ Saga badOrderSaga = service.execute(newSaga("bad_order"));
+ LOGGER.info("orders: goodOrder is {}, badOrder is {}",
+ goodOrderSaga.getResult(), badOrderSaga.getResult());
+
+ }
+
+
+ private static Saga newSaga(Object value) {
+ return Saga
+ .create()
+ .chapter("init an order").setInValue(value)
+ .chapter("booking a Fly")
+ .chapter("booking a Hotel")
+ .chapter("withdrawing Money");
+ }
+
+ private static ServiceDiscoveryService serviceDiscovery() {
+ ServiceDiscoveryService sd = new ServiceDiscoveryService();
+ return sd
+ .discover(new OrderService(sd))
+ .discover(new FlyBookingService(sd))
+ .discover(new HotelBookingService(sd))
+ .discover(new WithdrawMoneyService(sd));
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Service.java b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java
new file mode 100644
index 000000000..2e932b528
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java
@@ -0,0 +1,113 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.choreography;
+
+import java.util.function.Supplier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Common abstraction class representing services.
+ * implementing a general contract @see {@link ChoreographyChapter}
+ */
+public abstract class Service implements ChoreographyChapter {
+ protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class);
+
+ private final ServiceDiscoveryService sd;
+
+ public Service(ServiceDiscoveryService service) {
+ this.sd = service;
+ }
+
+ @Override
+ public Saga execute(Saga saga) {
+ Saga nextSaga = saga;
+ Object nextVal;
+ String chapterName = saga.getCurrent().getName();
+ if (chapterName.equals(getName())) {
+ if (saga.isForward()) {
+ nextSaga = process(saga);
+ nextVal = nextSaga.getCurrentValue();
+ if (nextSaga.isCurrentSuccess()) {
+ nextSaga.forward();
+ } else {
+ nextSaga.back();
+ }
+ } else {
+ nextSaga = rollback(saga);
+ nextVal = nextSaga.getCurrentValue();
+ nextSaga.back();
+ }
+
+ if (isSagaFinished(nextSaga)) {
+ return nextSaga;
+ }
+
+ nextSaga.setCurrentValue(nextVal);
+ }
+ Saga finalNextSaga = nextSaga;
+
+ return sd.find(chapterName).map(ch -> ch.execute(finalNextSaga))
+ .orElseThrow(serviceNotFoundException(chapterName));
+ }
+
+ private Supplier serviceNotFoundException(String chServiceName) {
+ return () -> new RuntimeException(
+ String.format("the service %s has not been found", chServiceName));
+ }
+
+ @Override
+ public Saga process(Saga saga) {
+ Object inValue = saga.getCurrentValue();
+ LOGGER.info("The chapter '{}' has been started. "
+ + "The data {} has been stored or calculated successfully",
+ getName(), inValue);
+ saga.setCurrentStatus(Saga.ChapterResult.SUCCESS);
+ saga.setCurrentValue(inValue);
+ return saga;
+ }
+
+ @Override
+ public Saga rollback(Saga saga) {
+ Object inValue = saga.getCurrentValue();
+ LOGGER.info("The Rollback for a chapter '{}' has been started. "
+ + "The data {} has been rollbacked successfully",
+ getName(), inValue);
+
+ saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK);
+ saga.setCurrentValue(inValue);
+ return saga;
+ }
+
+ private boolean isSagaFinished(Saga saga) {
+ if (!saga.isPresent()) {
+ saga.setFinished(true);
+ LOGGER.info(" the saga has been finished with {} status", saga.getResult());
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java
new file mode 100644
index 000000000..a616ff4a5
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java
@@ -0,0 +1,61 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.choreography;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+/**
+ * The class representing a service discovery pattern.
+ */
+public class ServiceDiscoveryService {
+ private Map services;
+
+ /**
+ * find any service.
+ *
+ * @return any service
+ * @throws NoSuchElementException if no elements further
+ */
+ public ChoreographyChapter findAny() {
+ return services.values().iterator().next();
+ }
+
+ public Optional find(String service) {
+ return Optional.ofNullable(services.getOrDefault(service, null));
+ }
+
+ public ServiceDiscoveryService discover(ChoreographyChapter chapterService) {
+ services.put(chapterService.getName(), chapterService);
+ return this;
+ }
+
+ public ServiceDiscoveryService() {
+ this.services = new HashMap<>();
+ }
+
+
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java
new file mode 100644
index 000000000..637e8ac1b
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.choreography;
+
+/**
+ * Class representing a service to withdraw a money.
+ */
+public class WithdrawMoneyService extends Service {
+
+ public WithdrawMoneyService(ServiceDiscoveryService service) {
+ super(service);
+ }
+
+ @Override
+ public String getName() {
+ return "withdrawing Money";
+ }
+
+ @Override
+ public Saga process(Saga saga) {
+ Object inValue = saga.getCurrentValue();
+
+ if (inValue.equals("bad_order")) {
+ LOGGER.info("The chapter '{}' has been started. But the exception has been raised."
+ + "The rollback is about to start",
+ getName(), inValue);
+ saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK);
+ return saga;
+ }
+ return super.process(saga);
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java
new file mode 100644
index 000000000..ef34ddb98
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java
@@ -0,0 +1,62 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+/**
+ * Executing result for chapter.
+ *
+ * @param incoming value
+ */
+public class ChapterResult {
+ private K value;
+ private State state;
+
+ public K getValue() {
+ return value;
+ }
+
+ ChapterResult(K value, State state) {
+ this.value = value;
+ this.state = state;
+ }
+
+ public boolean isSuccess() {
+ return state == State.SUCCESS;
+ }
+
+ public static ChapterResult success(K val) {
+ return new ChapterResult<>(val, State.SUCCESS);
+ }
+
+ public static ChapterResult failure(K val) {
+ return new ChapterResult<>(val, State.FAILURE);
+ }
+
+ /**
+ * state for chapter.
+ */
+ public enum State {
+ SUCCESS, FAILURE
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java
new file mode 100644
index 000000000..23b612957
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java
@@ -0,0 +1,34 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+/**
+ * Class representing a service to book a fly.
+ */
+public class FlyBookingService extends Service {
+ @Override
+ public String getName() {
+ return "booking a Fly";
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java
new file mode 100644
index 000000000..2d6ba1389
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java
@@ -0,0 +1,52 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+/**
+ * Class representing a service to book a hotel.
+ */
+public class HotelBookingService extends Service {
+ @Override
+ public String getName() {
+ return "booking a Hotel";
+ }
+
+
+ @Override
+ public ChapterResult rollback(String value) {
+ if (value.equals("crashed_order")) {
+ LOGGER.info("The Rollback for a chapter '{}' has been started. "
+ + "The data {} has been failed.The saga has been crashed.",
+ getName(), value);
+
+ return ChapterResult.failure(value);
+ }
+
+ LOGGER.info("The Rollback for a chapter '{}' has been started. "
+ + "The data {} has been rollbacked successfully",
+ getName(), value);
+
+ return super.rollback(value);
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java
new file mode 100644
index 000000000..7e9e3581f
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java
@@ -0,0 +1,55 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+/**
+ * ChoreographyChapter is an interface representing a contract for an external service.
+ *
+ * @param is type for passing params
+ */
+public interface OrchestrationChapter {
+
+ /**
+ * method get name.
+ * @return service name.
+ */
+ String getName();
+
+ /**
+ * The operation executed in general case.
+ *
+ * @param value incoming value
+ * @return result {@link ChapterResult}
+ */
+ ChapterResult process(K value);
+
+ /**
+ * The operation executed in rollback case.
+ *
+ * @param value incoming value
+ * @return result {@link ChapterResult}
+ */
+ ChapterResult rollback(K value);
+
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java
new file mode 100644
index 000000000..c42a9d7a1
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java
@@ -0,0 +1,34 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+/**
+ * Class representing a service to init a new order.
+ */
+public class OrderService extends Service {
+ @Override
+ public String getName() {
+ return "init an order";
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java
new file mode 100644
index 000000000..0d53362aa
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java
@@ -0,0 +1,84 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Saga representation.
+ * Saga consists of chapters.
+ * Every ChoreographyChapter is executed by a certain service.
+ */
+public class Saga {
+
+ private List chapters;
+
+
+ private Saga() {
+ this.chapters = new ArrayList<>();
+ }
+
+
+ public Saga chapter(String name) {
+ this.chapters.add(new Chapter(name));
+ return this;
+ }
+
+
+ public Chapter get(int idx) {
+ return chapters.get(idx);
+ }
+
+ public boolean isPresent(int idx) {
+ return idx >= 0 && idx < chapters.size();
+ }
+
+
+ public static Saga create() {
+ return new Saga();
+ }
+
+ /**
+ * result for saga.
+ */
+ public enum Result {
+ FINISHED, ROLLBACK, CRASHED
+ }
+
+ /**
+ * class represents chapter name.
+ */
+ public static class Chapter {
+ String name;
+
+ public Chapter(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java
new file mode 100644
index 000000000..830f5e653
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java
@@ -0,0 +1,87 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This pattern is used in distributed services to perform
+ * a group of operations atomically.
+ * This is an analog of transaction in a database but in terms
+ * of microservices architecture this is executed
+ * in a distributed environment
+ *
+ * A saga is a sequence of local transactions in a certain context.
+ * If one transaction fails for some reason,
+ * the saga executes compensating transactions(rollbacks)
+ * to undo the impact of the preceding transactions.
+ *
+ *
In this approach, there is an orchestrator @see {@link SagaOrchestrator}
+ * that manages all the transactions and directs
+ * the participant services to execute local transactions based on events.
+ * The major difference with choreography saga is an ability to handle crashed services
+ * (otherwise in choreography services very hard to prevent a saga
+ * if one of them has been crashed)
+ *
+ * @see Saga
+ * @see SagaOrchestrator
+ * @see Service
+ */
+public class SagaApplication {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class);
+
+ /**
+ * method to show common saga logic.
+ */
+ public static void main(String[] args) {
+ SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
+
+ Saga.Result goodOrder = sagaOrchestrator.execute("good_order");
+ Saga.Result badOrder = sagaOrchestrator.execute("bad_order");
+ Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order");
+
+ LOGGER.info("orders: goodOrder is {}, badOrder is {},crashedOrder is {}",
+ goodOrder, badOrder, crashedOrder);
+ }
+
+
+ private static Saga newSaga() {
+ return Saga
+ .create()
+ .chapter("init an order")
+ .chapter("booking a Fly")
+ .chapter("booking a Hotel")
+ .chapter("withdrawing Money");
+ }
+
+ private static ServiceDiscoveryService serviceDiscovery() {
+ return
+ new ServiceDiscoveryService()
+ .discover(new OrderService())
+ .discover(new FlyBookingService())
+ .discover(new HotelBookingService())
+ .discover(new WithdrawMoneyService());
+ }
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java
new file mode 100644
index 000000000..beec37655
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java
@@ -0,0 +1,148 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+import static com.iluwatar.saga.orchestration.Saga.Result.CRASHED;
+import static com.iluwatar.saga.orchestration.Saga.Result.FINISHED;
+import static com.iluwatar.saga.orchestration.Saga.Result.ROLLBACK;
+
+import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The orchestrator that manages all the transactions and directs
+ * the participant services to execute local transactions based on events.
+ */
+public class SagaOrchestrator {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SagaOrchestrator.class);
+ private final Saga saga;
+ private final ServiceDiscoveryService sd;
+ private final CurrentState state;
+
+
+ /**
+ * Create a new service to orchetrate sagas.
+ * @param saga saga to process
+ * @param sd service discovery @see {@link ServiceDiscoveryService}
+ */
+ public SagaOrchestrator(Saga saga, ServiceDiscoveryService sd) {
+ this.saga = saga;
+ this.sd = sd;
+ this.state = new CurrentState();
+ }
+
+ /**
+ * pipeline to execute saga process/story.
+ *
+ * @param value incoming value
+ * @param type for incoming value
+ * @return result @see {@link Saga.Result}
+ */
+ @SuppressWarnings("unchecked")
+ public Saga.Result execute(K value) {
+ state.cleanUp();
+ LOGGER.info(" The new saga is about to start");
+ Saga.Result result = FINISHED;
+ K tempVal = value;
+
+ while (true) {
+ int next = state.current();
+ Saga.Chapter ch = saga.get(next);
+ Optional srvOpt = sd.find(ch.name);
+
+ if (!srvOpt.isPresent()) {
+ state.directionToBack();
+ state.back();
+ continue;
+ }
+
+ OrchestrationChapter srv = srvOpt.get();
+
+ if (state.isForward()) {
+ ChapterResult processRes = srv.process(tempVal);
+ if (processRes.isSuccess()) {
+ next = state.forward();
+ tempVal = (K) processRes.getValue();
+ } else {
+ state.directionToBack();
+ }
+ } else {
+ ChapterResult rlRes = srv.rollback(tempVal);
+ if (rlRes.isSuccess()) {
+ next = state.back();
+ tempVal = (K) rlRes.getValue();
+ } else {
+ result = CRASHED;
+ next = state.back();
+ }
+ }
+
+
+ if (!saga.isPresent(next)) {
+ return state.isForward() ? FINISHED : result == CRASHED ? CRASHED : ROLLBACK;
+ }
+ }
+
+ }
+
+
+ private static class CurrentState {
+ int currentNumber;
+ boolean isForward;
+
+ void cleanUp() {
+ currentNumber = 0;
+ isForward = true;
+ }
+
+ CurrentState() {
+ this.currentNumber = 0;
+ this.isForward = true;
+ }
+
+
+ boolean isForward() {
+ return isForward;
+ }
+
+ void directionToBack() {
+ isForward = false;
+ }
+
+ int forward() {
+ return ++currentNumber;
+ }
+
+ int back() {
+ return --currentNumber;
+ }
+
+ int current() {
+ return currentNumber;
+ }
+ }
+
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java
new file mode 100644
index 000000000..d2b065201
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java
@@ -0,0 +1,59 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Common abstraction class representing services.
+ * implementing a general contract @see {@link OrchestrationChapter}
+ *
+ * @param type of incoming param
+ */
+public abstract class Service implements OrchestrationChapter {
+ protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class);
+
+ @Override
+ public abstract String getName();
+
+
+ @Override
+ public ChapterResult process(K value) {
+ LOGGER.info("The chapter '{}' has been started. "
+ + "The data {} has been stored or calculated successfully",
+ getName(), value);
+ return ChapterResult.success(value);
+ }
+
+ @Override
+ public ChapterResult rollback(K value) {
+ LOGGER.info("The Rollback for a chapter '{}' has been started. "
+ + "The data {} has been rollbacked successfully",
+ getName(), value);
+ return ChapterResult.success(value);
+ }
+
+
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java
new file mode 100644
index 000000000..dbc6e7eb5
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java
@@ -0,0 +1,50 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * The class representing a service discovery pattern.
+ */
+public class ServiceDiscoveryService {
+ private Map> services;
+
+ public Optional find(String service) {
+ return Optional.ofNullable(services.getOrDefault(service, null));
+ }
+
+ public ServiceDiscoveryService discover(OrchestrationChapter> orchestrationChapterService) {
+ services.put(orchestrationChapterService.getName(), orchestrationChapterService);
+ return this;
+ }
+
+ public ServiceDiscoveryService() {
+ this.services = new HashMap<>();
+ }
+
+
+}
diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java
new file mode 100644
index 000000000..7eb5634ef
--- /dev/null
+++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java
@@ -0,0 +1,45 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.iluwatar.saga.orchestration;
+
+/**
+ * Class representing a service to withdraw a money.
+ */
+public class WithdrawMoneyService extends Service {
+ @Override
+ public String getName() {
+ return "withdrawing Money";
+ }
+
+ @Override
+ public ChapterResult process(String value) {
+ if (value.equals("bad_order") || value.equals("crashed_order")) {
+ LOGGER.info("The chapter '{}' has been started. But the exception has been raised."
+ + "The rollback is about to start",
+ getName(), value);
+ return ChapterResult.failure(value);
+ }
+ return super.process(value);
+ }
+}
diff --git a/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java
new file mode 100644
index 000000000..d7a2a34b2
--- /dev/null
+++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java
@@ -0,0 +1,37 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.saga.choreography;
+
+import com.iluwatar.saga.orchestration.SagaApplication;
+import org.junit.Test;
+
+
+/***
+ * empty test
+ */
+public class SagaApplicationTest {
+ @Test
+ public void mainTest() {
+ SagaApplication.main(new String[]{});
+ }
+}
\ No newline at end of file
diff --git a/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java b/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java
new file mode 100644
index 000000000..4300d4057
--- /dev/null
+++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java
@@ -0,0 +1,62 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.saga.choreography;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * test to check choreography saga
+ */
+public class SagaChoreographyTest {
+
+
+ @Test
+ public void executeTest() {
+ ServiceDiscoveryService sd = serviceDiscovery();
+ ChoreographyChapter service = sd.findAny();
+ Saga badOrderSaga = service.execute(newSaga("bad_order"));
+ Saga goodOrderSaga = service.execute(newSaga("good_order"));
+
+ Assert.assertEquals(badOrderSaga.getResult(), Saga.SagaResult.ROLLBACKED);
+ Assert.assertEquals(goodOrderSaga.getResult(), Saga.SagaResult.FINISHED);
+ }
+
+ private static Saga newSaga(Object value) {
+ return Saga
+ .create()
+ .chapter("init an order").setInValue(value)
+ .chapter("booking a Fly")
+ .chapter("booking a Hotel")
+ .chapter("withdrawing Money");
+ }
+
+ private static ServiceDiscoveryService serviceDiscovery() {
+ ServiceDiscoveryService sd = new ServiceDiscoveryService();
+ return sd
+ .discover(new OrderService(sd))
+ .discover(new FlyBookingService(sd))
+ .discover(new HotelBookingService(sd))
+ .discover(new WithdrawMoneyService(sd));
+ }
+}
diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java
new file mode 100644
index 000000000..aa3c8773f
--- /dev/null
+++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java
@@ -0,0 +1,38 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.saga.orchestration;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * empty test
+ */
+public class SagaApplicationTest {
+
+ @Test
+ public void mainTest() {
+ SagaApplication.main(new String[]{});
+ }
+}
\ No newline at end of file
diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java
new file mode 100644
index 000000000..a93bf5280
--- /dev/null
+++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java
@@ -0,0 +1,145 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.saga.orchestration;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * test to test orchestration logic
+ */
+public class SagaOrchestratorInternallyTest {
+
+ private List records = new ArrayList<>();
+
+ @Test
+ public void executeTest() {
+ SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
+ Saga.Result result = sagaOrchestrator.execute(1);
+ Assert.assertEquals(result, Saga.Result.ROLLBACK);
+ Assert.assertArrayEquals(
+ records.toArray(new String[]{}),
+ new String[]{"+1", "+2", "+3", "+4", "-4", "-3", "-2", "-1"});
+ }
+
+ private static Saga newSaga() {
+ return Saga
+ .create()
+ .chapter("1")
+ .chapter("2")
+ .chapter("3")
+ .chapter("4");
+ }
+
+ private ServiceDiscoveryService serviceDiscovery() {
+ return
+ new ServiceDiscoveryService()
+ .discover(new Service1())
+ .discover(new Service2())
+ .discover(new Service3())
+ .discover(new Service4());
+ }
+
+ class Service1 extends Service {
+
+ @Override
+ public String getName() {
+ return "1";
+ }
+
+ @Override
+ public ChapterResult process(Integer value) {
+ records.add("+1");
+ return ChapterResult.success(value);
+ }
+
+ @Override
+ public ChapterResult rollback(Integer value) {
+ records.add("-1");
+ return ChapterResult.success(value);
+ }
+ }
+
+ class Service2 extends Service {
+
+ @Override
+ public String getName() {
+ return "2";
+ }
+
+ @Override
+ public ChapterResult process(Integer value) {
+ records.add("+2");
+ return ChapterResult.success(value);
+ }
+
+ @Override
+ public ChapterResult rollback(Integer value) {
+ records.add("-2");
+ return ChapterResult.success(value);
+ }
+ }
+
+ class Service3 extends Service {
+
+ @Override
+ public String getName() {
+ return "3";
+ }
+
+ @Override
+ public ChapterResult process(Integer value) {
+ records.add("+3");
+ return ChapterResult.success(value);
+ }
+
+ @Override
+ public ChapterResult rollback(Integer value) {
+ records.add("-3");
+ return ChapterResult.success(value);
+ }
+ }
+
+ class Service4 extends Service {
+
+ @Override
+ public String getName() {
+ return "4";
+ }
+
+ @Override
+ public ChapterResult process(Integer value) {
+ records.add("+4");
+ return ChapterResult.failure(value);
+ }
+
+ @Override
+ public ChapterResult rollback(Integer value) {
+ records.add("-4");
+ return ChapterResult.success(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java
new file mode 100644
index 000000000..d3522418c
--- /dev/null
+++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java
@@ -0,0 +1,60 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.saga.orchestration;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * test to check general logic
+ */
+public class SagaOrchestratorTest {
+
+ @Test
+ public void execute() {
+ SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
+ Saga.Result badOrder = sagaOrchestrator.execute("bad_order");
+ Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order");
+
+ Assert.assertEquals(badOrder, Saga.Result.ROLLBACK);
+ Assert.assertEquals(crashedOrder, Saga.Result.CRASHED);
+ }
+
+ private static Saga newSaga() {
+ return Saga
+ .create()
+ .chapter("init an order")
+ .chapter("booking a Fly")
+ .chapter("booking a Hotel")
+ .chapter("withdrawing Money");
+ }
+
+ private static ServiceDiscoveryService serviceDiscovery() {
+ return
+ new ServiceDiscoveryService()
+ .discover(new OrderService())
+ .discover(new FlyBookingService())
+ .discover(new HotelBookingService())
+ .discover(new WithdrawMoneyService());
+ }
+}
\ No newline at end of file