Merge branch 'master' of https://github.com/besok/java-design-patterns
This commit is contained in:
		
							
								
								
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -183,6 +183,8 @@ | |||||||
|         <module>data-locality</module> |         <module>data-locality</module> | ||||||
|         <module>subclass-sandbox</module> |         <module>subclass-sandbox</module> | ||||||
|         <module>circuit-breaker</module> |         <module>circuit-breaker</module> | ||||||
|  |         <module>role-object</module> | ||||||
|  | 		<module>saga</module> | ||||||
|         <module>double-buffer</module> |         <module>double-buffer</module> | ||||||
|         <module>sharding</module> |         <module>sharding</module> | ||||||
|     </modules> |     </modules> | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								saga/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								saga/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | --- | ||||||
|  | layout: pattern | ||||||
|  | title: Saga | ||||||
|  | folder: saga | ||||||
|  | permalink: /patterns/saga/ | ||||||
|  | categories: Behavioral | ||||||
|  | tags: | ||||||
|  |  - Java | ||||||
|  |  - Difficulty-Expert | ||||||
|  |  - Idiom | ||||||
|  |  - Distributed communication | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Also known as | ||||||
|  | This pattern has a similar goal with two-phase commit (XA transaction) | ||||||
|  |  | ||||||
|  | ## Intent | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | ## Explanation | ||||||
|  | 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. | ||||||
|  | There are two types of Saga: | ||||||
|  |  | ||||||
|  | - Choreography-Based Saga.  | ||||||
|  | In this approach, there is no central orchestrator.  | ||||||
|  | Each service participating in the Saga performs their transaction and publish events.  | ||||||
|  | The other services act upon those events and perform their transactions.  | ||||||
|  | Also, they may or not publish other events based on the situation. | ||||||
|  |  | ||||||
|  | - Orchestration-Based Saga | ||||||
|  | In this approach, there is a Saga orchestrator that manages all the transactions and directs  | ||||||
|  | the participant services to execute local transactions based on events.  | ||||||
|  | This orchestrator can also be though of as a Saga Manager. | ||||||
|  |  | ||||||
|  | ## Applicability | ||||||
|  | Use the Saga pattern, if: | ||||||
|  | - you need to perform a group of operations related to different microservices atomically | ||||||
|  | - you need to rollback changes in different places in case of failure one of the operation | ||||||
|  | - you need to take care of data consistency in different places including different databases | ||||||
|  | - you can not use 2PC(two phase commit) | ||||||
|  |  | ||||||
|  | ## Credits | ||||||
|  | - [Pattern: Saga](https://microservices.io/patterns/data/saga.html) | ||||||
							
								
								
									
										45
									
								
								saga/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								saga/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | <?xml version="1.0"?> | ||||||
|  | <!-- | ||||||
|  |  | ||||||
|  |     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. | ||||||
|  |  | ||||||
|  | --> | ||||||
|  | <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | ||||||
|  |          xmlns="http://maven.apache.org/POM/4.0.0" | ||||||
|  |          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||||||
|  |     <modelVersion>4.0.0</modelVersion> | ||||||
|  |     <parent> | ||||||
|  |         <groupId>com.iluwatar</groupId> | ||||||
|  |         <artifactId>java-design-patterns</artifactId> | ||||||
|  |         <version>1.22.0-SNAPSHOT</version> | ||||||
|  |     </parent> | ||||||
|  |  | ||||||
|  |     <artifactId>saga</artifactId> | ||||||
|  |     <dependencies> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>junit</groupId> | ||||||
|  |             <artifactId>junit</artifactId> | ||||||
|  |             <scope>test</scope> | ||||||
|  |         </dependency> | ||||||
|  |     </dependencies> | ||||||
|  |  | ||||||
|  | </project> | ||||||
| @@ -0,0 +1,65 @@ | |||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * ChoreographyChapter is an interface representing a contract for an external service. | ||||||
|  |  * 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 ChoreographyChapter { | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 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); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * get name method. | ||||||
|  |    * @return service name. | ||||||
|  |    */ | ||||||
|  |   String getName(); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * The operation executed in general case. | ||||||
|  |    * | ||||||
|  |    * @param saga incoming saga | ||||||
|  |    * @return result {@link Saga} | ||||||
|  |    */ | ||||||
|  |   Saga process(Saga saga); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * The operation executed in rollback case. | ||||||
|  |    * | ||||||
|  |    * @param saga incoming saga | ||||||
|  |    * @return result {@link Saga} | ||||||
|  |    */ | ||||||
|  |   Saga rollback(Saga saga); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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 book a fly. | ||||||
|  |  */ | ||||||
|  | public class FlyBookingService extends Service { | ||||||
|  |   public FlyBookingService(ServiceDiscoveryService service) { | ||||||
|  |     super(service); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public String getName() { | ||||||
|  |     return "booking a Fly"; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | /* | ||||||
|  |  * 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"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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 init a new order. | ||||||
|  |  */ | ||||||
|  | public class OrderService extends Service { | ||||||
|  |  | ||||||
|  |   public OrderService(ServiceDiscoveryService service) { | ||||||
|  |     super(service); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public String getName() { | ||||||
|  |     return "init an order"; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										217
									
								
								saga/src/main/java/com/iluwatar/saga/choreography/Saga.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								saga/src/main/java/com/iluwatar/saga/choreography/Saga.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | |||||||
|  | /* | ||||||
|  |  * 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.ArrayList; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Saga representation. | ||||||
|  |  * Saga consists of chapters. | ||||||
|  |  * Every ChoreographyChapter is executed a certain service. | ||||||
|  |  */ | ||||||
|  | public class Saga { | ||||||
|  |  | ||||||
|  |   private List<Chapter> 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 | ||||||
|  |         + '}'; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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 | ||||||
|  |  * | ||||||
|  |  * <p>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. | ||||||
|  |  * | ||||||
|  |  * <p>In this approach, there are no mediators or orchestrators services. | ||||||
|  |  * All chapters are handled and moved by services manually. | ||||||
|  |  * | ||||||
|  |  * <p>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)); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								saga/src/main/java/com/iluwatar/saga/choreography/Service.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								saga/src/main/java/com/iluwatar/saga/choreography/Service.java
									
									
									
									
									
										Normal file
									
								
							| @@ -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<RuntimeException> 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; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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<String, ChoreographyChapter> services; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * find any service. | ||||||
|  |    * | ||||||
|  |    * @return any service | ||||||
|  |    * @throws NoSuchElementException if no elements further | ||||||
|  |    */ | ||||||
|  |   public ChoreographyChapter findAny() { | ||||||
|  |     return services.values().iterator().next(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public Optional<ChoreographyChapter> 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<>(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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 <K> incoming value | ||||||
|  |  */ | ||||||
|  | public class ChapterResult<K> { | ||||||
|  |   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 <K> ChapterResult<K> success(K val) { | ||||||
|  |     return new ChapterResult<>(val, State.SUCCESS); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public static <K> ChapterResult<K> failure(K val) { | ||||||
|  |     return new ChapterResult<>(val, State.FAILURE); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * state for chapter. | ||||||
|  |    */ | ||||||
|  |   public enum State { | ||||||
|  |     SUCCESS, FAILURE | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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<String> { | ||||||
|  |   @Override | ||||||
|  |   public String getName() { | ||||||
|  |     return "booking a Fly"; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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<String> { | ||||||
|  |   @Override | ||||||
|  |   public String getName() { | ||||||
|  |     return "booking a Hotel"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public ChapterResult<String> 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); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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 <K> is type for passing params | ||||||
|  |  */ | ||||||
|  | public interface OrchestrationChapter<K> { | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * method get name. | ||||||
|  |    * @return service name. | ||||||
|  |    */ | ||||||
|  |   String getName(); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * The operation executed in general case. | ||||||
|  |    * | ||||||
|  |    * @param value incoming value | ||||||
|  |    * @return result {@link ChapterResult} | ||||||
|  |    */ | ||||||
|  |   ChapterResult<K> process(K value); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * The operation executed in rollback case. | ||||||
|  |    * | ||||||
|  |    * @param value incoming value | ||||||
|  |    * @return result {@link ChapterResult} | ||||||
|  |    */ | ||||||
|  |   ChapterResult<K> rollback(K value); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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<String> { | ||||||
|  |   @Override | ||||||
|  |   public String getName() { | ||||||
|  |     return "init an order"; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Chapter> 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; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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 | ||||||
|  |  * | ||||||
|  |  * <p>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. | ||||||
|  |  * | ||||||
|  |  * <p>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()); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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 <K>   type for incoming value | ||||||
|  |    * @return result @see {@link Saga.Result} | ||||||
|  |    */ | ||||||
|  |   @SuppressWarnings("unchecked") | ||||||
|  |   public <K> 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<OrchestrationChapter> 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; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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 <K> type of incoming param | ||||||
|  |  */ | ||||||
|  | public abstract class Service<K> implements OrchestrationChapter<K> { | ||||||
|  |   protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class); | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public abstract String getName(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public ChapterResult<K> 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<K> 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); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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<String, OrchestrationChapter<?>> services; | ||||||
|  |  | ||||||
|  |   public Optional<OrchestrationChapter> 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<>(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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<String> { | ||||||
|  |   @Override | ||||||
|  |   public String getName() { | ||||||
|  |     return "withdrawing Money"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public ChapterResult<String> 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); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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[]{}); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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)); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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[]{}); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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<String> 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<Integer> { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getName() { | ||||||
|  |       return "1"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> process(Integer value) { | ||||||
|  |       records.add("+1"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> rollback(Integer value) { | ||||||
|  |       records.add("-1"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   class Service2 extends Service<Integer> { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getName() { | ||||||
|  |       return "2"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> process(Integer value) { | ||||||
|  |       records.add("+2"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> rollback(Integer value) { | ||||||
|  |       records.add("-2"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   class Service3 extends Service<Integer> { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getName() { | ||||||
|  |       return "3"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> process(Integer value) { | ||||||
|  |       records.add("+3"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> rollback(Integer value) { | ||||||
|  |       records.add("-3"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   class Service4 extends Service<Integer> { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getName() { | ||||||
|  |       return "4"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> process(Integer value) { | ||||||
|  |       records.add("+4"); | ||||||
|  |       return ChapterResult.failure(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChapterResult<Integer> rollback(Integer value) { | ||||||
|  |       records.add("-4"); | ||||||
|  |       return ChapterResult.success(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -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()); | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user