diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java index f610b9f62..51b0ce6b8 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java @@ -22,13 +22,20 @@ */ package com.iluwatar.saga.choreography; -import com.iluwatar.saga.orchestration.ChapterResult; /** * Chapter is an interface representing a contract for an external service. - * @param is type for passing params - */ -public interface Chapter { + * In that case, a service needs to make a decision what to do further + * hence the server needs to get all context representing {@link Saga} + * */ +public interface Chapter { + + /** + * In that case, every method is responsible to make a decision on what to do then + * @param saga incoming saga + * @return saga result + */ + Saga execute(Saga saga); /** * @return service name. @@ -37,17 +44,17 @@ public interface Chapter { /** * The operation executed in general case. - * @param value incoming value - * @return result {@link ChapterResult} + * @param saga incoming saga + * @return result {@link Saga} */ - ChapterResult process(K value); + Saga process(Saga saga); /** * The operation executed in rollback case. - * @param value incoming value - * @return result {@link ChapterResult} + * @param saga incoming saga + * @return result {@link Saga} */ - ChapterResult rollback(K value); + Saga rollback(Saga saga); } diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java new file mode 100644 index 000000000..591324c52 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.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.choreography; + + +/** + * Class representing a service to book a fly + */ +public class FlyBookingService extends Service { + public FlyBookingService(ServiceDiscoveryService service) { + super(service); + } + + @Override + public String getName() { + return "booking a Fly"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java new file mode 100644 index 000000000..e822a3568 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java @@ -0,0 +1,40 @@ +/* + * 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 book a hotel + */ +public class HotelBookingService extends Service { + public HotelBookingService(ServiceDiscoveryService service) { + super(service); + } + + @Override + public String getName() { + return "booking a Hotel"; + } + + + } diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java b/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java new file mode 100644 index 000000000..6c8b9912e --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java @@ -0,0 +1,39 @@ +/* + * 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 init a new order. + */ +public class OrderService extends Service{ + + public OrderService(ServiceDiscoveryService service) { + super(service); + } + + @Override + public String getName() { + return "init an order"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java index 94c8a119d..34a8d6a0f 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java @@ -22,6 +22,10 @@ */ package com.iluwatar.saga.choreography; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Saga representation. * Saga consists of chapters. @@ -29,6 +33,127 @@ package com.iluwatar.saga.choreography; */ public class Saga { + private List chapters; + private int pos; + private boolean forward; + private boolean finished; + public static Saga create() { + return new Saga(); + } + public SagaResult getResult() { + return !finished ? + SagaResult.PROGRESS : + forward ? + SagaResult.FINISHED : + SagaResult.ROLLBACKED; + } + public Saga chapter(String name) { + this.chapters.add(new Chapter(name)); + return this; + } + public Saga setInValue(Object value){ + if(chapters.isEmpty()){ + return this; + } + chapters.get(chapters.size()-1).setInValue(value); + return this; + } + public Object getCurrentValue(){ + return chapters.get(pos).getInValue(); + } + public void setCurrentValue(Object value){ + chapters.get(pos).setInValue(value); + } + 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; + } + + + public 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; + } + public void setResult(ChapterResult result){ + this.result = result; + } + + public boolean isSuccess(){ + return result == ChapterResult.SUCCESS; + } + } + + + public enum ChapterResult { + INIT, SUCCESS, ROLLBACK + } + + 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..46792adac --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java @@ -0,0 +1,77 @@ +/* + * 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); + + public static void main(String[] args) { + ServiceDiscoveryService sd = serviceDiscovery(); + Chapter 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..0f182e570 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java @@ -0,0 +1,103 @@ +/* + * 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; + +/** + * Common abstraction class representing services + * implementing a general contract @see {@link Chapter} + */ +public abstract class Service implements Chapter { + 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(RuntimeException::new); + } + + @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..11512d112 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.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.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 Chapter findAny(){ + return services.values().iterator().next(); + } + + public Optional find(String service) { + return Optional.ofNullable(services.getOrDefault(service, null)); + } + + public ServiceDiscoveryService discover(Chapter 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..c13b1b1f8 --- /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/Saga.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java index d6a703706..f24a8366c 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -28,7 +28,7 @@ import java.util.List; /** * Saga representation. * Saga consists of chapters. - * Every Chapter is executed a certain service. + * Every Chapter is executed by a certain service. */ public class Saga { @@ -39,7 +39,6 @@ public class Saga { } - public Saga chapter(String name) { this.chapters.add(new Chapter(name)); return this; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java index 013d9a5f0..bbeca62bb 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -24,7 +24,6 @@ package com.iluwatar.saga.orchestration; -import com.iluwatar.saga.ServiceDiscoveryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +37,8 @@ import org.slf4j.LoggerFactory; * * 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 @@ -49,7 +50,7 @@ public class SagaApplication { public static void main(String[] args) { SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); - Saga.Result goodOrder = sagaOrchestrator.execute("god_order"); + Saga.Result goodOrder = sagaOrchestrator.execute("good_order"); Saga.Result badOrder = sagaOrchestrator.execute("bad_order"); Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order"); diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java index 588a860aa..c005d70df 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -22,7 +22,6 @@ */ package com.iluwatar.saga.orchestration; -import com.iluwatar.saga.ServiceDiscoveryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,6 +47,7 @@ public class SagaOrchestrator { } /** + * pipeline to execute saga process/story * * @param value incoming value * @param type for incoming value diff --git a/saga/src/main/java/com/iluwatar/saga/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java similarity index 95% rename from saga/src/main/java/com/iluwatar/saga/ServiceDiscoveryService.java rename to saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java index f9d91b53e..652740051 100644 --- a/saga/src/main/java/com/iluwatar/saga/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java @@ -20,9 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.saga; - -import com.iluwatar.saga.orchestration.Chapter; +package com.iluwatar.saga.orchestration; import java.util.HashMap; import java.util.Map; 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..5802f54ec --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java @@ -0,0 +1,35 @@ +/* + * 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; + +import static org.junit.Assert.*; + +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..8c8daa7c9 --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.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.choreography; + +import org.junit.Assert; +import org.junit.Test; + +public class SagaChoreographyTest { + + + @Test + public void executeTest() { + ServiceDiscoveryService sd = serviceDiscovery(); + Chapter 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 index 6950158aa..4e4d86644 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java @@ -1,3 +1,25 @@ +/* + * 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; diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java index 16c54c920..ce3b926a3 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java @@ -1,6 +1,27 @@ +/* + * 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 com.iluwatar.saga.ServiceDiscoveryService; import org.junit.Assert; import org.junit.Test; @@ -12,7 +33,7 @@ public class SagaOrchestratorInternallyTest { private List records = new ArrayList<>(); @Test - public void execute() { + public void executeTest() { SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); Saga.Result result = sagaOrchestrator.execute(1); Assert.assertEquals(result, Saga.Result.ROLLBACK); diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java index cd659601e..bead0343d 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java @@ -1,6 +1,27 @@ +/* + * 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 com.iluwatar.saga.ServiceDiscoveryService; import org.junit.Assert; import org.junit.Test;