From a204383f4571480cccd1d6802e72b89d630dfe5d Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 19 Oct 2019 16:17:59 +0100 Subject: [PATCH 01/19] init repo for role object --- role-object/README.md | 20 ++++++++++++++++++++ role-object/pom.xml | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 role-object/README.md create mode 100644 role-object/pom.xml diff --git a/role-object/README.md b/role-object/README.md new file mode 100644 index 000000000..8ffe47400 --- /dev/null +++ b/role-object/README.md @@ -0,0 +1,20 @@ +--- +layout: pattern +title: Role object +folder: Migration +permalink: /patterns/role-object/ +categories: Behavioral +tags: + - Java + - Easy-to-implement +--- + +## Also known as + +## Intent + +## Applicability + +## Real world examples + +## Credits diff --git a/role-object/pom.xml b/role-object/pom.xml new file mode 100644 index 000000000..9d7e9b88b --- /dev/null +++ b/role-object/pom.xml @@ -0,0 +1,38 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.22.0-SNAPSHOT + + + role-object + + + From c3656109a78ecad0604b92b21bf538866016dbee Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 19 Oct 2019 16:23:39 +0100 Subject: [PATCH 02/19] add to init --- .../com/iluwatar/roleobject/ApplicationRoleObject.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java diff --git a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java new file mode 100644 index 000000000..89388099f --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java @@ -0,0 +1,7 @@ +package com.iluwatar.roleobject; + +public class ApplicationRoleObject { + public static void main(String[] args) { + System.out.println("Role-object"); + } +} From f09a7eb468731ca32d099223d3e1c00221ed259a Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 19 Oct 2019 18:15:20 +0100 Subject: [PATCH 03/19] add to init --- role-object/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/role-object/README.md b/role-object/README.md index 8ffe47400..44f992289 100644 --- a/role-object/README.md +++ b/role-object/README.md @@ -3,13 +3,15 @@ layout: pattern title: Role object folder: Migration permalink: /patterns/role-object/ -categories: Behavioral +categories: Structural tags: - Java - - Easy-to-implement + - Difficulty-Medium + - Handle Body Pattern --- ## Also known as +Post pattern, Extension Object pattern ## Intent From 20b4195fb2bd0c5a6bff4280c758a4e6b4e3ef6e Mon Sep 17 00:00:00 2001 From: Besok Date: Sun, 20 Oct 2019 20:22:54 +0100 Subject: [PATCH 04/19] add first impl --- role-object/README.md | 17 +++++- .../roleobject/ApplicationRoleObject.java | 55 ++++++++++++++++++- .../com/iluwatar/roleobject/BorrowerRole.java | 4 ++ .../com/iluwatar/roleobject/Customer.java | 12 ++++ .../com/iluwatar/roleobject/CustomerCore.java | 35 ++++++++++++ .../com/iluwatar/roleobject/CustomerRole.java | 25 +++++++++ .../com/iluwatar/roleobject/InvestorRole.java | 4 ++ .../java/com/iluwatar/roleobject/Role.java | 30 ++++++++++ 8 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/Customer.java create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java create mode 100644 role-object/src/main/java/com/iluwatar/roleobject/Role.java diff --git a/role-object/README.md b/role-object/README.md index 44f992289..3f71341e1 100644 --- a/role-object/README.md +++ b/role-object/README.md @@ -14,9 +14,22 @@ tags: Post pattern, Extension Object pattern ## Intent +Adapt an object to different client’s needs through transparently attached role objects, each one representing a role +the object has to play in that client’s context. The object manages its role set dynamically. By representing roles as +individual objects, different contexts are kept separate and system configuration is simplified. ## Applicability - -## Real world examples +Use the Role Object pattern, if: +- you want to handle a key abstraction in different contexts and you do not want to put the resulting contextspecific interfaces into the same class interface. +Words: 4895 Page 3 of 11 +- you want to handle the available roles dynamically so that they can be attached and removed on demand, that is +at runtime, rather than fixing them statically at compile-time. +- you want to treat the extensions transparently and need to preserve the logical object identity of the resulting +object conglomerate. +- you want to keep role/client pairs independent from each other so that changes to a role do not affect clients +that are not interested in that role. ## Credits +- [Hillside - Role object pattern](https://hillside.net/plop/plop97/Proceedings/riehle.pdf) +- [Role object](http://wiki.c2.com/?RoleObject) +- [Fowler - Dealing with roles](https://martinfowler.com/apsupp/roles.pdf) \ No newline at end of file diff --git a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java index 89388099f..3dc83e470 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java @@ -1,7 +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.roleobject; +/** + * The Role Object pattern suggests to model context-specific views + * of an object as separate role objects which are + * dynamically attached to and removed from the core object. + * We call the resulting composite object structure, + * consisting of the core and its role objects, a subject. + * A subject often plays several roles and the same role is likely to + * be played by different subjects. + * As an example consider two different customers playing the role of borrower and + * investor, respectively. Both roles could as well be played by a single Customer object. + * The common superclass for customer-specific roles is provided by CustomerRole, + * which also supports the Customer interface. + * The CustomerRole class is abstract and not meant to be instantiated. + * Concrete subclasses of CustomerRole, for example Borrower or Investor, + * define and implement the interface for specific roles. It is only + * these subclasses which are instantiated at runtime. + * The Borrower class defines the context-specific view of + * Customer objects as needed by the loan department. + * It defines additional operations to manage the customer’s + * credits and securities. Similarly, the Investor class adds operations specific + * to the investment department’s view of customers. + * A client like the loan application may either work with objects of the CustomerCore class, using the interface class + * Customer, or with objects of concrete CustomerRole subclasses. Suppose the loan application knows a particular + * Customer instance through its Customer interface. The loan application may want to check whether the Customer + * object plays the role of Borrower. + * To this end it calls hasRole() with a suitable role specification. For the purpose of + * our example, let’s assume we can name roles with a simple string. + * If the Customer object can play the role named + * “Borrower,” the loan application will ask it to return a reference to the corresponding object. + * The loan application may now use this reference to call Borrower-specific operations. + * + */ public class ApplicationRoleObject { public static void main(String[] args) { - System.out.println("Role-object"); } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java new file mode 100644 index 000000000..c900e3eed --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java @@ -0,0 +1,4 @@ +package com.iluwatar.roleobject; + +public class BorrowerRole extends CustomerRole{ +} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java new file mode 100644 index 000000000..c9cdab4e1 --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java @@ -0,0 +1,12 @@ +package com.iluwatar.roleobject; + +import java.util.Optional; + +public abstract class Customer { + + public abstract boolean addRole(Role role); + public abstract boolean hasRole(Role role); + public abstract boolean remRole(Role role); + public abstract Optional getRole(Role role,Class expectedRole); + +} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java new file mode 100644 index 000000000..496022c06 --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java @@ -0,0 +1,35 @@ +package com.iluwatar.roleobject; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class CustomerCore extends Customer { + + private Map roles; + + + @Override + public boolean addRole(Role role) { + return role.instance() + .map(rI -> roles.put(role, rI)) + .isPresent(); + } + + @Override + public boolean hasRole(Role role) { + return roles.containsKey(role); + } + + @Override + public boolean remRole(Role role) { + return Objects.nonNull(roles.remove(role)); + } + + @Override + public Optional getRole(Role role, Class expectedRole) { + return Optional.ofNullable(roles.get(role)) + .filter(expectedRole::isInstance) + .map(expectedRole::cast); + } +} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java new file mode 100644 index 000000000..af2801f3d --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java @@ -0,0 +1,25 @@ +package com.iluwatar.roleobject; + +import java.util.Optional; + +public class CustomerRole extends Customer{ + @Override + public boolean addRole(Role role) { + return false; + } + + @Override + public boolean hasRole(Role role) { + return false; + } + + @Override + public boolean remRole(Role role) { + return false; + } + + @Override + public Optional getRole(Role role, Class expectedRole) { + return Optional.empty(); + } +} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java new file mode 100644 index 000000000..a8a85d9da --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java @@ -0,0 +1,4 @@ +package com.iluwatar.roleobject; + +public class InvestorRole extends CustomerRole{ +} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Role.java b/role-object/src/main/java/com/iluwatar/roleobject/Role.java new file mode 100644 index 000000000..a59d6377b --- /dev/null +++ b/role-object/src/main/java/com/iluwatar/roleobject/Role.java @@ -0,0 +1,30 @@ +package com.iluwatar.roleobject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sun.rmi.runtime.Log; + +import java.util.Optional; + +public enum Role { + Borrower(BorrowerRole.class), Investor(InvestorRole.class); + + private Class typeCst; + + Role(Class typeCst) { + this.typeCst = typeCst; + } + private static final Logger logger = LoggerFactory.getLogger(Role.class); + + @SuppressWarnings("unchecked") + Optional instance(){ + Class typeCst = this.typeCst; + try { + return (Optional) Optional.of(typeCst.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + logger.error("error creating an object",e); + } + return Optional.empty(); + } + +} From 630532cdda100036a2d9e8553b76af2a157d9b82 Mon Sep 17 00:00:00 2001 From: Besok Date: Thu, 24 Oct 2019 20:56:09 +0100 Subject: [PATCH 05/19] add pattern --- role-object/README.md | 12 +-- role-object/pom.xml | 8 +- .../roleobject/ApplicationRoleObject.java | 44 ++++++++-- .../com/iluwatar/roleobject/BorrowerRole.java | 15 ++++ .../com/iluwatar/roleobject/Customer.java | 47 ++++++++++- .../com/iluwatar/roleobject/CustomerCore.java | 32 ++++++-- .../com/iluwatar/roleobject/CustomerRole.java | 27 +------ .../com/iluwatar/roleobject/InvestorRole.java | 25 +++++- .../java/com/iluwatar/roleobject/Role.java | 15 ++-- .../roleobject/ApplicationRoleObjectTest.java | 13 +++ .../iluwatar/roleobject/BorrowerRoleTest.java | 18 +++++ .../iluwatar/roleobject/CustomerCoreTest.java | 81 +++++++++++++++++++ .../iluwatar/roleobject/InvestorRoleTest.java | 18 +++++ .../com/iluwatar/roleobject/RoleTest.java | 18 +++++ 14 files changed, 320 insertions(+), 53 deletions(-) create mode 100644 role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java create mode 100644 role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java create mode 100644 role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java create mode 100644 role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java create mode 100644 role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java diff --git a/role-object/README.md b/role-object/README.md index 3f71341e1..c97177d7b 100644 --- a/role-object/README.md +++ b/role-object/README.md @@ -20,14 +20,10 @@ individual objects, different contexts are kept separate and system configuratio ## Applicability Use the Role Object pattern, if: -- you want to handle a key abstraction in different contexts and you do not want to put the resulting contextspecific interfaces into the same class interface. -Words: 4895 Page 3 of 11 -- you want to handle the available roles dynamically so that they can be attached and removed on demand, that is -at runtime, rather than fixing them statically at compile-time. -- you want to treat the extensions transparently and need to preserve the logical object identity of the resulting -object conglomerate. -- you want to keep role/client pairs independent from each other so that changes to a role do not affect clients -that are not interested in that role. +- you want to handle a key abstraction in different contexts and you do not want to put the resulting context specific interfaces into the same class interface. +- you want to handle the available roles dynamically so that they can be attached and removed on demand, that is at runtime, rather than fixing them statically at compile-time. +- you want to treat the extensions transparently and need to preserve the logical object identity of the resultingobject conglomerate. +- you want to keep role/client pairs independent from each other so that changes to a role do not affect clients that are not interested in that role. ## Credits - [Hillside - Role object pattern](https://hillside.net/plop/plop97/Proceedings/riehle.pdf) diff --git a/role-object/pom.xml b/role-object/pom.xml index 9d7e9b88b..c9feb1419 100644 --- a/role-object/pom.xml +++ b/role-object/pom.xml @@ -33,6 +33,12 @@ role-object - + + + junit + junit + test + + diff --git a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java index 3dc83e470..1006fe084 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java @@ -22,6 +22,11 @@ */ package com.iluwatar.roleobject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.iluwatar.roleobject.Role.*; + /** * The Role Object pattern suggests to model context-specific views * of an object as separate role objects which are @@ -34,12 +39,12 @@ package com.iluwatar.roleobject; * investor, respectively. Both roles could as well be played by a single Customer object. * The common superclass for customer-specific roles is provided by CustomerRole, * which also supports the Customer interface. + *

* The CustomerRole class is abstract and not meant to be instantiated. * Concrete subclasses of CustomerRole, for example Borrower or Investor, * define and implement the interface for specific roles. It is only * these subclasses which are instantiated at runtime. - * The Borrower class defines the context-specific view of - * Customer objects as needed by the loan department. + * The Borrower class defines the context-specific view of Customer objects as needed by the loan department. * It defines additional operations to manage the customer’s * credits and securities. Similarly, the Investor class adds operations specific * to the investment department’s view of customers. @@ -48,13 +53,40 @@ package com.iluwatar.roleobject; * Customer instance through its Customer interface. The loan application may want to check whether the Customer * object plays the role of Borrower. * To this end it calls hasRole() with a suitable role specification. For the purpose of - * our example, let’s assume we can name roles with a simple string. - * If the Customer object can play the role named - * “Borrower,” the loan application will ask it to return a reference to the corresponding object. + * our example, let’s assume we can name roles with enum. + * If the Customer object can play the role named “Borrower,” the loan application will ask it to return a reference to the corresponding object. * The loan application may now use this reference to call Borrower-specific operations. - * */ public class ApplicationRoleObject { + + private static final Logger logger = LoggerFactory.getLogger(Role.class); + public static void main(String[] args) { + Customer customer = Customer.newCustomer(Borrower, Investor); + + logger.info(" the new customer created : {}", customer); + + boolean hasBorrowerRole = customer.hasRole(Borrower); + logger.info(" customer has a borrowed role - {}", hasBorrowerRole); + boolean hasInvestorRole = customer.hasRole(Investor); + logger.info(" customer has an investor role - {}", hasInvestorRole); + + customer.getRole(Investor, InvestorRole.class) + .ifPresent(inv -> { + inv.setAmountToInvest(1000); + inv.setName("Billy"); + }); + customer.getRole(Borrower, BorrowerRole.class) + .ifPresent(inv -> inv.setName("Johny")); + + customer.getRole(Investor, InvestorRole.class) + .map(InvestorRole::invest) + .ifPresent(logger::info); + + customer.getRole(Borrower, BorrowerRole.class) + .map(BorrowerRole::borrow) + .ifPresent(logger::info); } + + } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java index c900e3eed..8bc423e4b 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java @@ -1,4 +1,19 @@ package com.iluwatar.roleobject; public class BorrowerRole extends CustomerRole{ + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String borrow(){ + return String.join(" ", + "A borrower",name,"wants to get some money."); + } + } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java index c9cdab4e1..c8bf5a857 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java @@ -2,11 +2,56 @@ package com.iluwatar.roleobject; import java.util.Optional; +/** + * The main abstraction to work with Customer. + */ public abstract class Customer { + /** + * Add specific role @see {@link Role} + * + * @param role to add + * @return true if the operation has been successful otherwise false + */ public abstract boolean addRole(Role role); + + /** + * Check specific role @see {@link Role} + * + * @param role to check + * @return true if the role exists otherwise false + */ + public abstract boolean hasRole(Role role); + + /** + * Remove specific role @see {@link Role} + * + * @param role to remove + * @return true if the operation has been successful otherwise false + */ public abstract boolean remRole(Role role); - public abstract Optional getRole(Role role,Class expectedRole); + + /** + * Get specific instance associated with this role @see {@link Role} + * + * @param role to get + * @param expectedRole instance class expected to get + * @return optional with value if the instance exists and corresponds expected class + */ + public abstract Optional getRole(Role role, Class expectedRole); + + + public static Customer newCustomer() { + return new CustomerCore(); + } + + public static Customer newCustomer(Role... role) { + Customer customer = newCustomer(); + for (Role r : role) { + customer.addRole(r); + } + return customer; + } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java index 496022c06..3c2c7d406 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java @@ -1,19 +1,30 @@ package com.iluwatar.roleobject; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +/** + * Core class to store different customer roles + * + * @see CustomerRole + * Note: not thread safe + */ public class CustomerCore extends Customer { private Map roles; + public CustomerCore() { + roles = new HashMap<>(); + } @Override public boolean addRole(Role role) { - return role.instance() - .map(rI -> roles.put(role, rI)) - .isPresent(); + return role + .instance() + .map(inst -> { + roles.put(role, inst); + return true; + }) + .orElse(false); } @Override @@ -28,8 +39,15 @@ public class CustomerCore extends Customer { @Override public Optional getRole(Role role, Class expectedRole) { - return Optional.ofNullable(roles.get(role)) + return Optional + .ofNullable(roles.get(role)) .filter(expectedRole::isInstance) .map(expectedRole::cast); } + + @Override + public String toString() { + String roles = Arrays.toString(this.roles.keySet().toArray()); + return "Customer{roles=" + roles + "}"; + } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java index af2801f3d..e9806246b 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java @@ -1,25 +1,6 @@ package com.iluwatar.roleobject; -import java.util.Optional; - -public class CustomerRole extends Customer{ - @Override - public boolean addRole(Role role) { - return false; - } - - @Override - public boolean hasRole(Role role) { - return false; - } - - @Override - public boolean remRole(Role role) { - return false; - } - - @Override - public Optional getRole(Role role, Class expectedRole) { - return Optional.empty(); - } -} +/** + * Key abstraction for segregated roles + */ +public abstract class CustomerRole extends CustomerCore{} diff --git a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java index a8a85d9da..4267d4d95 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java @@ -1,4 +1,27 @@ package com.iluwatar.roleobject; -public class InvestorRole extends CustomerRole{ +public class InvestorRole extends CustomerRole { + private String name; + private long amountToInvest; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getAmountToInvest() { + return amountToInvest; + } + + public void setAmountToInvest(long amountToInvest) { + this.amountToInvest = amountToInvest; + } + + public String invest() { + return String.join(" ", + "Investor", name, "has invested", String.valueOf(amountToInvest), "dollars"); + } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Role.java b/role-object/src/main/java/com/iluwatar/roleobject/Role.java index a59d6377b..c1934f7c5 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/Role.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/Role.java @@ -2,27 +2,30 @@ package com.iluwatar.roleobject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sun.rmi.runtime.Log; import java.util.Optional; +/** + * Possible roles + */ public enum Role { Borrower(BorrowerRole.class), Investor(InvestorRole.class); - private Class typeCst; + private Class typeCst; - Role(Class typeCst) { + Role(Class typeCst) { this.typeCst = typeCst; } + private static final Logger logger = LoggerFactory.getLogger(Role.class); @SuppressWarnings("unchecked") - Optional instance(){ - Class typeCst = this.typeCst; + public Optional instance() { + Class typeCst = this.typeCst; try { return (Optional) Optional.of(typeCst.newInstance()); } catch (InstantiationException | IllegalAccessException e) { - logger.error("error creating an object",e); + logger.error("error creating an object", e); } return Optional.empty(); } diff --git a/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java b/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java new file mode 100644 index 000000000..f2c822912 --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java @@ -0,0 +1,13 @@ +package com.iluwatar.roleobject; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ApplicationRoleObjectTest { + + @Test + public void mainTest() { + ApplicationRoleObject.main(new String[]{}); + } +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java new file mode 100644 index 000000000..6ba26009f --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.roleobject; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class BorrowerRoleTest { + + @Test + public void borrowTest() { + BorrowerRole borrowerRole = new BorrowerRole(); + borrowerRole.setName("test"); + String res = "A borrower test wants to get some money."; + + Assert.assertEquals(borrowerRole.borrow(),res); + } +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java b/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java new file mode 100644 index 000000000..64712e11d --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java @@ -0,0 +1,81 @@ +package com.iluwatar.roleobject; + +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.*; + +public class CustomerCoreTest { + + @Test + public void addRole() { + CustomerCore core = new CustomerCore(); + boolean add = core.addRole(Role.Borrower); + assertTrue(add); + + } + + @Test + public void hasRole() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + + boolean has = core.hasRole(Role.Borrower); + assertTrue(has); + + boolean notHas = core.hasRole(Role.Investor); + assertFalse(notHas); + } + + @Test + public void remRole() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + + Optional bRole = core.getRole(Role.Borrower, BorrowerRole.class); + assertTrue(bRole.isPresent()); + + boolean res = core.remRole(Role.Borrower); + assertTrue(res); + + Optional empt = core.getRole(Role.Borrower, BorrowerRole.class); + assertFalse(empt.isPresent()); + + } + + @Test + public void getRole() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + + Optional bRole = core.getRole(Role.Borrower, BorrowerRole.class); + assertTrue(bRole.isPresent()); + + Optional nonRole = core.getRole(Role.Borrower, InvestorRole.class); + assertFalse(nonRole.isPresent()); + + Optional invRole = core.getRole(Role.Investor, InvestorRole.class); + assertFalse(invRole.isPresent()); + + + } + + + @Test + public void toStringTest() { + CustomerCore core = new CustomerCore(); + core.addRole(Role.Borrower); + assertEquals(core.toString(), "Customer{roles=[Borrower]}"); + + core = new CustomerCore(); + core.addRole(Role.Investor); + assertEquals(core.toString(), "Customer{roles=[Investor]}"); + + core = new CustomerCore(); + assertEquals(core.toString(), "Customer{roles=[]}"); + + + } + +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java new file mode 100644 index 000000000..9274eae7c --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.roleobject; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class InvestorRoleTest { + + @Test + public void investTest() { + InvestorRole investorRole = new InvestorRole(); + investorRole.setName("test"); + investorRole.setAmountToInvest(10); + String res = "Investor test has invested 10 dollars"; + Assert.assertEquals(investorRole.invest(), res); + } +} \ No newline at end of file diff --git a/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java new file mode 100644 index 000000000..dda326228 --- /dev/null +++ b/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.roleobject; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.*; + +public class RoleTest { + + @Test + public void instanceTest() { + Optional instance = Role.Borrower.instance(); + Assert.assertTrue(instance.isPresent()); + Assert.assertEquals(instance.get().getClass(),BorrowerRole.class); + } +} \ No newline at end of file From 9403d53096a63a8f2e4e0e8db6a153af621bebeb Mon Sep 17 00:00:00 2001 From: Besok Date: Thu, 24 Oct 2019 20:59:21 +0100 Subject: [PATCH 06/19] add license --- .../com/iluwatar/roleobject/BorrowerRole.java | 22 +++++++++++++++++ .../com/iluwatar/roleobject/Customer.java | 22 +++++++++++++++++ .../com/iluwatar/roleobject/CustomerCore.java | 22 +++++++++++++++++ .../com/iluwatar/roleobject/CustomerRole.java | 22 +++++++++++++++++ .../com/iluwatar/roleobject/InvestorRole.java | 22 +++++++++++++++++ .../java/com/iluwatar/roleobject/Role.java | 22 +++++++++++++++++ .../roleobject/ApplicationRoleObjectTest.java | 24 +++++++++++++++++-- .../iluwatar/roleobject/BorrowerRoleTest.java | 22 +++++++++++++++++ .../iluwatar/roleobject/CustomerCoreTest.java | 22 +++++++++++++++++ .../iluwatar/roleobject/InvestorRoleTest.java | 24 +++++++++++++++++-- .../com/iluwatar/roleobject/RoleTest.java | 22 +++++++++++++++++ 11 files changed, 242 insertions(+), 4 deletions(-) diff --git a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java index 8bc423e4b..c48829a0a 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.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.roleobject; public class BorrowerRole extends CustomerRole{ diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java b/role-object/src/main/java/com/iluwatar/roleobject/Customer.java index c8bf5a857..ebcddff4b 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/Customer.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/Customer.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.roleobject; import java.util.Optional; diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java index 3c2c7d406..5de27aa92 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerCore.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.roleobject; import java.util.*; diff --git a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java index e9806246b..40fe2341b 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/CustomerRole.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.roleobject; /** diff --git a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java index 4267d4d95..9f3ecdae3 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.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.roleobject; public class InvestorRole extends CustomerRole { diff --git a/role-object/src/main/java/com/iluwatar/roleobject/Role.java b/role-object/src/main/java/com/iluwatar/roleobject/Role.java index c1934f7c5..f6c739891 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/Role.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/Role.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.roleobject; import org.slf4j.Logger; diff --git a/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java b/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java index f2c822912..831781d71 100644 --- a/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java +++ b/role-object/src/test/java/com/iluwatar/roleobject/ApplicationRoleObjectTest.java @@ -1,9 +1,29 @@ +/* + * 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.roleobject; import org.junit.Test; -import static org.junit.Assert.*; - public class ApplicationRoleObjectTest { @Test diff --git a/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java index 6ba26009f..1641e20fa 100644 --- a/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java +++ b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.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.roleobject; import org.junit.Assert; diff --git a/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java b/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java index 64712e11d..1b2987400 100644 --- a/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.java +++ b/role-object/src/test/java/com/iluwatar/roleobject/CustomerCoreTest.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.roleobject; import org.junit.Test; diff --git a/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java index 9274eae7c..06afa1016 100644 --- a/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java +++ b/role-object/src/test/java/com/iluwatar/roleobject/InvestorRoleTest.java @@ -1,10 +1,30 @@ +/* + * 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.roleobject; import org.junit.Assert; import org.junit.Test; -import static org.junit.Assert.*; - public class InvestorRoleTest { @Test diff --git a/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java index dda326228..6ae5b0cd8 100644 --- a/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.java +++ b/role-object/src/test/java/com/iluwatar/roleobject/RoleTest.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.roleobject; import org.junit.Assert; From e3827945c8bbd0bc39c49b16960da03360c851ee Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 26 Oct 2019 18:28:20 +0100 Subject: [PATCH 07/19] add changes --- .../roleobject/ApplicationRoleObject.java | 25 ++++++++++--------- .../com/iluwatar/roleobject/BorrowerRole.java | 3 +-- .../com/iluwatar/roleobject/InvestorRole.java | 3 +-- .../iluwatar/roleobject/BorrowerRoleTest.java | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java index 1006fe084..b8296daba 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/ApplicationRoleObject.java @@ -36,25 +36,26 @@ import static com.iluwatar.roleobject.Role.*; * A subject often plays several roles and the same role is likely to * be played by different subjects. * As an example consider two different customers playing the role of borrower and - * investor, respectively. Both roles could as well be played by a single Customer object. - * The common superclass for customer-specific roles is provided by CustomerRole, - * which also supports the Customer interface. + * investor, respectively. Both roles could as well be played by a single {@link Customer} object. + * The common superclass for customer-specific roles is provided by {@link CustomerRole}, + * which also supports the {@link Customer} interface. *

- * The CustomerRole class is abstract and not meant to be instantiated. - * Concrete subclasses of CustomerRole, for example Borrower or Investor, + * The {@link CustomerRole} class is abstract and not meant to be instantiated. + * Concrete subclasses of {@link CustomerRole}, for example {@link BorrowerRole} or {@link InvestorRole}, * define and implement the interface for specific roles. It is only * these subclasses which are instantiated at runtime. - * The Borrower class defines the context-specific view of Customer objects as needed by the loan department. + * The {@link BorrowerRole} class defines the context-specific view of {@link Customer} objects as needed by the loan department. * It defines additional operations to manage the customer’s - * credits and securities. Similarly, the Investor class adds operations specific + * credits and securities. Similarly, the {@link InvestorRole} class adds operations specific * to the investment department’s view of customers. - * A client like the loan application may either work with objects of the CustomerCore class, using the interface class - * Customer, or with objects of concrete CustomerRole subclasses. Suppose the loan application knows a particular - * Customer instance through its Customer interface. The loan application may want to check whether the Customer + * A client like the loan application may either work with objects of the {@link CustomerRole} class, using the interface class + * {@link Customer}, or with objects of concrete {@link CustomerRole} subclasses. Suppose the loan application knows a particular + * {@link Customer} instance through its {@link Customer} interface. The loan application may want to check whether the {@link Customer} * object plays the role of Borrower. - * To this end it calls hasRole() with a suitable role specification. For the purpose of + * To this end it calls {@link Customer#hasRole(Role)} with a suitable role specification. For the purpose of * our example, let’s assume we can name roles with enum. - * If the Customer object can play the role named “Borrower,” the loan application will ask it to return a reference to the corresponding object. + * If the {@link Customer} object can play the role named “Borrower,” the loan application will ask it + * to return a reference to the corresponding object. * The loan application may now use this reference to call Borrower-specific operations. */ public class ApplicationRoleObject { diff --git a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java index c48829a0a..425d9511d 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/BorrowerRole.java @@ -34,8 +34,7 @@ public class BorrowerRole extends CustomerRole{ } public String borrow(){ - return String.join(" ", - "A borrower",name,"wants to get some money."); + return String.format("Borrower %s wants to get some money.",name); } } diff --git a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java index 9f3ecdae3..6d5c17c90 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java @@ -43,7 +43,6 @@ public class InvestorRole extends CustomerRole { } public String invest() { - return String.join(" ", - "Investor", name, "has invested", String.valueOf(amountToInvest), "dollars"); + return String.format("Investor %s has invested %d dollars", name, amountToInvest); } } diff --git a/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java index 1641e20fa..0c0f92fc2 100644 --- a/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java +++ b/role-object/src/test/java/com/iluwatar/roleobject/BorrowerRoleTest.java @@ -33,7 +33,7 @@ public class BorrowerRoleTest { public void borrowTest() { BorrowerRole borrowerRole = new BorrowerRole(); borrowerRole.setName("test"); - String res = "A borrower test wants to get some money."; + String res = "Borrower test wants to get some money."; Assert.assertEquals(borrowerRole.borrow(),res); } From 768e6471085635c66c8128b100e628e9fcdae29b Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 2 Nov 2019 11:29:52 +0000 Subject: [PATCH 08/19] add saga init dsc --- pom.xml | 2 + saga/README.md | 45 +++++++++++ saga/pom.xml | 44 +++++++++++ stirring-clicker/pom.xml | 28 +++++++ .../ie/home/besok/stirrings/Application.java | 15 ++++ .../java/ie/home/besok/stirrings/Counter.java | 14 ++++ .../ie/home/besok/stirrings/FileStorage.java | 37 +++++++++ .../java/ie/home/besok/stirrings/Gui.java | 73 ++++++++++++++++++ stirring-clicker/src/main/resources/1.png | Bin 0 -> 4434 bytes stirring-clicker/src/main/resources/2.jpg | Bin 0 -> 3603 bytes stirring-clicker/src/main/resources/3.jpg | Bin 0 -> 2903 bytes .../home/besok/stirrings/FileStorageTest.java | 25 ++++++ 12 files changed, 283 insertions(+) create mode 100644 saga/README.md create mode 100644 saga/pom.xml create mode 100644 stirring-clicker/pom.xml create mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java create mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java create mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java create mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java create mode 100644 stirring-clicker/src/main/resources/1.png create mode 100644 stirring-clicker/src/main/resources/2.jpg create mode 100644 stirring-clicker/src/main/resources/3.jpg create mode 100644 stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java diff --git a/pom.xml b/pom.xml index 22dedc14a..4e8b76c78 100644 --- a/pom.xml +++ b/pom.xml @@ -183,6 +183,8 @@ data-locality subclass-sandbox circuit-breaker + role-object + saga diff --git a/saga/README.md b/saga/README.md new file mode 100644 index 000000000..12e4c8482 --- /dev/null +++ b/saga/README.md @@ -0,0 +1,45 @@ +--- +layout: pattern +title: Saga +folder: Communication +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 performed 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 description](https://microservices.io/patterns/data/saga.html) \ No newline at end of file diff --git a/saga/pom.xml b/saga/pom.xml new file mode 100644 index 000000000..26c331237 --- /dev/null +++ b/saga/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.22.0-SNAPSHOT + + + saga + + + junit + junit + test + + + + diff --git a/stirring-clicker/pom.xml b/stirring-clicker/pom.xml new file mode 100644 index 000000000..b0d223016 --- /dev/null +++ b/stirring-clicker/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + ie.home.besok + move-clicker + 1.0 + + 1.8 + 1.8 + + + + + org.projectlombok + lombok + 1.18.10 + + + + junit + junit + 4.12 + + + \ No newline at end of file diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java new file mode 100644 index 000000000..0835470bc --- /dev/null +++ b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java @@ -0,0 +1,15 @@ +package ie.home.besok.stirrings; + +import java.awt.*; +import java.io.IOException; + +public class Application { + public static void main(String[] args) throws IOException { + FileStorage storage = new FileStorage(); + + EventQueue.invokeLater(() -> { + Gui gui = new Gui(storage); + gui.setVisible(true); + }); + } +} diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java new file mode 100644 index 000000000..b0f93f9d7 --- /dev/null +++ b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java @@ -0,0 +1,14 @@ +package ie.home.besok.stirrings; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class Counter { + public Map count(List dates){ + return null; + } +} diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java new file mode 100644 index 000000000..c5784eb50 --- /dev/null +++ b/stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java @@ -0,0 +1,37 @@ +package ie.home.besok.stirrings; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.util.List; + +public class FileStorage { + + private Path file; + + public FileStorage() throws IOException { + this.file = Paths.get("data.log"); + if(!Files.exists(file)){ + Files.createFile(file); + } + } + + public void plus() { + String line = LocalDateTime.now().toString()+System.lineSeparator(); + try { + Files.write(file, line.getBytes(), StandardOpenOption.APPEND); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public List get() throws IOException { + return Files.readAllLines(file); + } + +} diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java new file mode 100644 index 000000000..3df3e2131 --- /dev/null +++ b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java @@ -0,0 +1,73 @@ +package ie.home.besok.stirrings; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.io.IOException; +import java.net.URL; + +public class Gui extends JFrame { + + private FileStorage storage; + + public Gui(FileStorage storage) { + this.storage = storage; + try { + createUI(storage); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void createUI(FileStorage storage) throws IOException { + setTitle("Stirring counter"); + setSize(300, 300); + setLocationRelativeTo(null); + + + JButton button = createButton(storage); + JButton graphick = new JButton(); + graphick.setIcon(getIcon("3.jpg")); + + + Container pane = getContentPane(); + GroupLayout gl = new GroupLayout(pane); + pane.setLayout(gl); + + gl.setAutoCreateContainerGaps(true); + + gl.setHorizontalGroup( + gl.createSequentialGroup().addComponent(button).addComponent(graphick) + ); + + gl.setVerticalGroup(gl.createSequentialGroup().addComponent(button).addComponent(graphick)); + + + button.addActionListener((event) -> { + storage.plus(); + try { + JOptionPane.showMessageDialog(null,"","",JOptionPane.INFORMATION_MESSAGE, getIcon("2.jpg")); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + + private JButton createButton(FileStorage storage) throws IOException { + ImageIcon babyIcon = getIcon("1.png"); + + JButton button = new JButton(); + + button.setIcon(babyIcon); + return button; + } + + private ImageIcon getIcon(String name) throws IOException { + URL file = this.getClass().getClassLoader().getResource(name); + return new ImageIcon(ImageIO.read(file)); + } + + +} diff --git a/stirring-clicker/src/main/resources/1.png b/stirring-clicker/src/main/resources/1.png new file mode 100644 index 0000000000000000000000000000000000000000..1ff5944d67256918b8cc12d43bda1aadf8040a82 GIT binary patch literal 4434 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 Y0pTRxRwVqi+W-In07*qoM6N<$g05LsLjV8( literal 0 HcmV?d00001 diff --git a/stirring-clicker/src/main/resources/2.jpg b/stirring-clicker/src/main/resources/2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6220fba24faf9895b96e999a0063de7fef8a8fd2 GIT binary patch literal 3603 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 LXche5;*I?spOLmY literal 0 HcmV?d00001 diff --git a/stirring-clicker/src/main/resources/3.jpg b/stirring-clicker/src/main/resources/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f2b5f6443e42014ce3fb1048422fcf2bc6a0e72a GIT binary patch literal 2903 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 literal 0 HcmV?d00001 diff --git a/stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java b/stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java new file mode 100644 index 000000000..1eaaba6d5 --- /dev/null +++ b/stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java @@ -0,0 +1,25 @@ +package ie.home.besok.stirrings; + +import org.junit.Test; + +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.*; + +public class FileStorageTest { + + @Test + public void fsTest() throws IOException { + FileStorage fs = new FileStorage(); + List arrs = fs.get(); + int oldSize = arrs.size(); + fs.plus(); + fs.plus(); + fs.plus(); + fs.plus(); + arrs = fs.get(); + int newSize = arrs.size(); + assertEquals(4, newSize - oldSize); + } +} \ No newline at end of file From 564cf1239f22f7b352b8fc2da20a7c9c137a4b4c Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 2 Nov 2019 11:30:41 +0000 Subject: [PATCH 09/19] add init saga dsc --- stirring-clicker/pom.xml | 28 ------- .../ie/home/besok/stirrings/Application.java | 15 ---- .../java/ie/home/besok/stirrings/Counter.java | 14 ---- .../ie/home/besok/stirrings/FileStorage.java | 37 --------- .../java/ie/home/besok/stirrings/Gui.java | 73 ------------------ stirring-clicker/src/main/resources/1.png | Bin 4434 -> 0 bytes stirring-clicker/src/main/resources/2.jpg | Bin 3603 -> 0 bytes stirring-clicker/src/main/resources/3.jpg | Bin 2903 -> 0 bytes .../home/besok/stirrings/FileStorageTest.java | 25 ------ 9 files changed, 192 deletions(-) delete mode 100644 stirring-clicker/pom.xml delete mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java delete mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java delete mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java delete mode 100644 stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java delete mode 100644 stirring-clicker/src/main/resources/1.png delete mode 100644 stirring-clicker/src/main/resources/2.jpg delete mode 100644 stirring-clicker/src/main/resources/3.jpg delete mode 100644 stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java diff --git a/stirring-clicker/pom.xml b/stirring-clicker/pom.xml deleted file mode 100644 index b0d223016..000000000 --- a/stirring-clicker/pom.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - 4.0.0 - - ie.home.besok - move-clicker - 1.0 - - 1.8 - 1.8 - - - - - org.projectlombok - lombok - 1.18.10 - - - - junit - junit - 4.12 - - - \ No newline at end of file diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java deleted file mode 100644 index 0835470bc..000000000 --- a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Application.java +++ /dev/null @@ -1,15 +0,0 @@ -package ie.home.besok.stirrings; - -import java.awt.*; -import java.io.IOException; - -public class Application { - public static void main(String[] args) throws IOException { - FileStorage storage = new FileStorage(); - - EventQueue.invokeLater(() -> { - Gui gui = new Gui(storage); - gui.setVisible(true); - }); - } -} diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java deleted file mode 100644 index b0f93f9d7..000000000 --- a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Counter.java +++ /dev/null @@ -1,14 +0,0 @@ -package ie.home.besok.stirrings; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class Counter { - public Map count(List dates){ - return null; - } -} diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java deleted file mode 100644 index c5784eb50..000000000 --- a/stirring-clicker/src/main/java/ie/home/besok/stirrings/FileStorage.java +++ /dev/null @@ -1,37 +0,0 @@ -package ie.home.besok.stirrings; - -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.time.LocalDateTime; -import java.util.List; - -public class FileStorage { - - private Path file; - - public FileStorage() throws IOException { - this.file = Paths.get("data.log"); - if(!Files.exists(file)){ - Files.createFile(file); - } - } - - public void plus() { - String line = LocalDateTime.now().toString()+System.lineSeparator(); - try { - Files.write(file, line.getBytes(), StandardOpenOption.APPEND); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public List get() throws IOException { - return Files.readAllLines(file); - } - -} diff --git a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java b/stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java deleted file mode 100644 index 3df3e2131..000000000 --- a/stirring-clicker/src/main/java/ie/home/besok/stirrings/Gui.java +++ /dev/null @@ -1,73 +0,0 @@ -package ie.home.besok.stirrings; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.io.IOException; -import java.net.URL; - -public class Gui extends JFrame { - - private FileStorage storage; - - public Gui(FileStorage storage) { - this.storage = storage; - try { - createUI(storage); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void createUI(FileStorage storage) throws IOException { - setTitle("Stirring counter"); - setSize(300, 300); - setLocationRelativeTo(null); - - - JButton button = createButton(storage); - JButton graphick = new JButton(); - graphick.setIcon(getIcon("3.jpg")); - - - Container pane = getContentPane(); - GroupLayout gl = new GroupLayout(pane); - pane.setLayout(gl); - - gl.setAutoCreateContainerGaps(true); - - gl.setHorizontalGroup( - gl.createSequentialGroup().addComponent(button).addComponent(graphick) - ); - - gl.setVerticalGroup(gl.createSequentialGroup().addComponent(button).addComponent(graphick)); - - - button.addActionListener((event) -> { - storage.plus(); - try { - JOptionPane.showMessageDialog(null,"","",JOptionPane.INFORMATION_MESSAGE, getIcon("2.jpg")); - } catch (IOException e) { - e.printStackTrace(); - } - }); - - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - - private JButton createButton(FileStorage storage) throws IOException { - ImageIcon babyIcon = getIcon("1.png"); - - JButton button = new JButton(); - - button.setIcon(babyIcon); - return button; - } - - private ImageIcon getIcon(String name) throws IOException { - URL file = this.getClass().getClassLoader().getResource(name); - return new ImageIcon(ImageIO.read(file)); - } - - -} diff --git a/stirring-clicker/src/main/resources/1.png b/stirring-clicker/src/main/resources/1.png deleted file mode 100644 index 1ff5944d67256918b8cc12d43bda1aadf8040a82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4434 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 Y0pTRxRwVqi+W-In07*qoM6N<$g05LsLjV8( diff --git a/stirring-clicker/src/main/resources/2.jpg b/stirring-clicker/src/main/resources/2.jpg deleted file mode 100644 index 6220fba24faf9895b96e999a0063de7fef8a8fd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3603 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 LXche5;*I?spOLmY diff --git a/stirring-clicker/src/main/resources/3.jpg b/stirring-clicker/src/main/resources/3.jpg deleted file mode 100644 index f2b5f6443e42014ce3fb1048422fcf2bc6a0e72a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2903 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 diff --git a/stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java b/stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java deleted file mode 100644 index 1eaaba6d5..000000000 --- a/stirring-clicker/src/test/java/ie/home/besok/stirrings/FileStorageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package ie.home.besok.stirrings; - -import org.junit.Test; - -import java.io.IOException; -import java.util.List; - -import static org.junit.Assert.*; - -public class FileStorageTest { - - @Test - public void fsTest() throws IOException { - FileStorage fs = new FileStorage(); - List arrs = fs.get(); - int oldSize = arrs.size(); - fs.plus(); - fs.plus(); - fs.plus(); - fs.plus(); - arrs = fs.get(); - int newSize = arrs.size(); - assertEquals(4, newSize - oldSize); - } -} \ No newline at end of file From 7ac47b291d5cd9d68fd291acdce2ade5297c0701 Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 2 Nov 2019 11:39:02 +0000 Subject: [PATCH 10/19] add changes to dsc --- saga/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/saga/README.md b/saga/README.md index 12e4c8482..276f12ee1 100644 --- a/saga/README.md +++ b/saga/README.md @@ -16,7 +16,8 @@ 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 performed in a distributed environment +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, From c4c37d77e9cdc9514b48193aad14426c878e530e Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 2 Nov 2019 14:45:05 +0000 Subject: [PATCH 11/19] add --- .../iluwatar/saga/orchestration/Chapter.java | 35 ++++++++ .../saga/orchestration/ChapterResult.java | 22 +++++ .../saga/orchestration/FlyBookingService.java | 8 ++ .../orchestration/HotelBookingService.java | 8 ++ .../saga/orchestration/OrderService.java | 8 ++ .../com/iluwatar/saga/orchestration/Saga.java | 80 +++++++++++++++++++ .../saga/orchestration/SagaApplication.java | 50 ++++++++++++ .../saga/orchestration/SagaException.java | 29 +++++++ .../saga/orchestration/SagaOrchestrator.java | 30 +++++++ .../iluwatar/saga/orchestration/Service.java | 33 ++++++++ .../ServiceDiscoveryService.java | 46 +++++++++++ .../iluwatar/saga/orchestration/Utility.java | 16 ++++ .../orchestration/WithdrawMoneyService.java | 16 ++++ 13 files changed, 381 insertions(+) create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/Service.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java create mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java new file mode 100644 index 000000000..9b1e1feac --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.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.orchestration; + +/** + * Chapter including into Saga + */ +public interface Chapter { + + String getName(); + + ChapterResult process(K value); + ChapterResult rollback(K value); + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java new file mode 100644 index 000000000..ee5ae8ba1 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java @@ -0,0 +1,22 @@ +package com.iluwatar.saga.orchestration; + +public class ChapterResult { + K value; + State state; + + public ChapterResult(K value, State state) { + this.value = value; + this.state = state; + } + + public static ChapterResult success(K val) { + return new ChapterResult<>(val, State.SUCCESS); + } + public static ChapterResult failure(K val) { + return new ChapterResult<>(val, State.FAILURE); + } + + public enum State { + SUCCESS, FAILURE + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java new file mode 100644 index 000000000..7a78b5f17 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -0,0 +1,8 @@ +package com.iluwatar.saga.orchestration; + +public class FlyBookingService extends Service { + @Override + public String getName() { + return "booking a Fly"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java new file mode 100644 index 000000000..8f294cd40 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -0,0 +1,8 @@ +package com.iluwatar.saga.orchestration; + +public class HotelBookingService extends Service { + @Override + public String getName() { + return "booking a Hotel"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java new file mode 100644 index 000000000..d5d79ac41 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -0,0 +1,8 @@ +package com.iluwatar.saga.orchestration; + +public class OrderService extends Service { + @Override + public String getName() { + return "init an order"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java new file mode 100644 index 000000000..b74d68959 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -0,0 +1,80 @@ +/* + * 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; + +public class Saga { + + private List chapters; + private Result result; + + public Saga() { + this.chapters = new ArrayList<>(); + this.result = Result.INPROCESS; + } + + public void setResult(Result result) { + this.result = result; + } + + public boolean isSuccess(){ + return result == Result.FINISHED; + } + + public boolean isFinished(){ + return result != Result.INPROCESS; + } + + public Saga chapter(String name, int timeoutInSec) { + this.chapters.add(new Chapter(name, timeoutInSec)); + return this; + } + + public Chapter get(int idx) { + if (chapters.size() < idx || idx < 0) { + throw new SagaException("idx shoud be less then ch size or more then 0"); + } + return chapters.get(idx); + } + + + + public static Saga create() { + return new Saga(); + } + + public enum Result{ + FINISHED,CANCELED, INPROCESS; + } + private static class Chapter { + String name; + int timeoutInSec; + + public Chapter(String name, int timeoutInSec) { + this.name = name; + this.timeoutInSec = timeoutInSec; + } + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java new file mode 100644 index 000000000..154f97bab --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + + +public class SagaApplication { + public static void main(String[] args) { + + } + + + + private Saga createSaga() { + return Saga.create() + .chapter("init an order",10) + .chapter("booking a Fly",10) + .chapter("booking a Hotel",10) + .chapter("withdrawing Money",10); + } + + private static ServiceDiscoveryService sd() { + return + new ServiceDiscoveryService() + .discover(new OrderService()) + .discover(new FlyBookingService()) + .discover(new HotelBookingService()) + .discover(new WithdrawMoneyService()); + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java new file mode 100644 index 000000000..0055bb85e --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java @@ -0,0 +1,29 @@ +/* + * 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; + +public class SagaException extends RuntimeException { + public SagaException(String message) { + super(message); + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java new file mode 100644 index 000000000..edc94bb22 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -0,0 +1,30 @@ +package com.iluwatar.saga.orchestration; + +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class SagaOrchestrator { + + private ExecutorService executor; + private AtomicBoolean isNormalFlow; + private AtomicBoolean isFinished; + private AtomicInteger currentState; + private final Saga saga; + + public SagaOrchestrator(Saga saga) { + this.saga = saga; + this.isNormalFlow = new AtomicBoolean(true); + this.isFinished = new AtomicBoolean(false); + this.currentState = new AtomicInteger(0); + this.executor = Executors.newFixedThreadPool(20); + } + + public Saga.Result kickOff(K value) { + + } + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java new file mode 100644 index 000000000..31a2ab702 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java @@ -0,0 +1,33 @@ +package com.iluwatar.saga.orchestration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.iluwatar.saga.orchestration.Utility.sleepInSec; + +public abstract class Service implements Chapter { + private static final Logger logger = LoggerFactory.getLogger(Service.class); + + @Override + public abstract String getName(); + + + @Override + public ChapterResult process(K value) { + logger.info("The chapter about {} is starting", getName()); + logger.info("storing or calculating a data {} to db", value); + sleepInSec(1); + logger.info("the data {} has been stored or calculated successfully", value); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(K value) { + logger.info("Something is going wrong hence The chapter about {} is starting the rollback procedure", getName()); + sleepInSec(1); + logger.info("the data {} has been rollbacked successfully", value); + return ChapterResult.success(value); + } + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java new file mode 100644 index 000000000..522f3f944 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java @@ -0,0 +1,46 @@ +/* + * 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; + +public class ServiceDiscoveryService { + private Map> services; + + 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/orchestration/Utility.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java new file mode 100644 index 000000000..bf5e902c0 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java @@ -0,0 +1,16 @@ +package com.iluwatar.saga.orchestration; + +public class Utility { + private Utility() { + } + + public static void sleepInSec(int sec){ + try { + Thread.sleep(sec*1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java new file mode 100644 index 000000000..25c7710dc --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -0,0 +1,16 @@ +package com.iluwatar.saga.orchestration; + +public class WithdrawMoneyService extends Service { + @Override + public String getName() { + return "withdrawing Money"; + } + + @Override + public ChapterResult process(String value) { + if(value.equals("no-money")){ + return ChapterResult.failure(value); + } + return super.process(value); + } +} From 3a4677c56d19a1b5d1044cf32b535f729141ec99 Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 2 Nov 2019 20:52:59 +0000 Subject: [PATCH 12/19] add orchestrator --- .../iluwatar/saga/orchestration/Chapter.java | 17 ++- .../saga/orchestration/ChapterResult.java | 41 ++++- .../saga/orchestration/FlyBookingService.java | 25 ++++ .../orchestration/HotelBookingService.java | 42 ++++++ .../saga/orchestration/OrderService.java | 25 ++++ .../com/iluwatar/saga/orchestration/Saga.java | 42 +++--- .../saga/orchestration/SagaApplication.java | 42 +++++- .../saga/orchestration/SagaException.java | 29 ---- .../saga/orchestration/SagaOrchestrator.java | 141 ++++++++++++++++-- .../iluwatar/saga/orchestration/Service.java | 42 ++++-- .../ServiceDiscoveryService.java | 5 +- .../iluwatar/saga/orchestration/Utility.java | 16 -- .../orchestration/WithdrawMoneyService.java | 30 +++- .../orchestration/SagaApplicationTest.java | 13 ++ .../SagaOrchestratorInternallyTest.java | 117 +++++++++++++++ .../orchestration/SagaOrchestratorTest.java | 37 +++++ 16 files changed, 555 insertions(+), 109 deletions(-) delete mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java delete mode 100644 saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java create mode 100644 saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java create mode 100644 saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java create mode 100644 saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java index 9b1e1feac..961a88937 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java @@ -23,13 +23,28 @@ package com.iluwatar.saga.orchestration; /** - * Chapter including into Saga + * Chapter is an interface representing a contract for an external service. + * @param is type for passing params */ public interface Chapter { + /** + * @return service name. + */ String getName(); + /** + * The operation executed in general case. + * @param value incoming value + * @return result {@link ChapterResult} + */ ChapterResult process(K value); + + /** + * The operation executed in rollback case. + * @param value incoming value + * @return result {@link ChapterResult} + */ ChapterResult rollback(K value); } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java index ee5ae8ba1..eae95b339 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java @@ -1,17 +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; +/** + * Executing result for chapter + * @param incoming value + */ public class ChapterResult { - K value; - State state; + private K value; + private State state; - public ChapterResult(K value, State state) { + public K getValue() { + return value; + } + + ChapterResult(K value, State state) { this.value = value; this.state = state; } + public boolean isSuccess(){ + return state == State.SUCCESS; + } + public static ChapterResult success(K val) { return new ChapterResult<>(val, State.SUCCESS); } + public static ChapterResult failure(K val) { return new ChapterResult<>(val, State.FAILURE); } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java index 7a78b5f17..6cb8479c0 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -1,5 +1,30 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.saga.orchestration; +/** + * Class representing a service to book a fly + */ public class FlyBookingService extends Service { @Override public String getName() { diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java index 8f294cd40..e21046328 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -1,8 +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; +/** + * Class representing a service to book a hotel + */ public class HotelBookingService extends Service { @Override public String getName() { return "booking a Hotel"; } + + + @Override + public ChapterResult rollback(String value) { + if(value.equals("crashed_order")){ + logger.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been failed.The saga has been crashed.", + getName(), value); + + return ChapterResult.failure(value); + } + + logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + getName(), value); + + return super.rollback(value); + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java index d5d79ac41..6edd94ace 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -1,5 +1,30 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.saga.orchestration; +/** + * Class representing a service to init a new order. + */ public class OrderService extends Service { @Override public String getName() { 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 b74d68959..c21435f20 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -25,56 +25,50 @@ package com.iluwatar.saga.orchestration; import java.util.ArrayList; import java.util.List; +/** + * Saga representation. + * Saca consists of chapters. + * Every Chapter is executed a certain service. + */ public class Saga { private List chapters; - private Result result; public Saga() { this.chapters = new ArrayList<>(); - this.result = Result.INPROCESS; } - public void setResult(Result result) { - this.result = result; - } - public boolean isSuccess(){ - return result == Result.FINISHED; - } - public boolean isFinished(){ - return result != Result.INPROCESS; - } - - public Saga chapter(String name, int timeoutInSec) { - this.chapters.add(new Chapter(name, timeoutInSec)); + public Saga chapter(String name) { + this.chapters.add(new Chapter(name)); return this; } + public Chapter get(int idx) { - if (chapters.size() < idx || idx < 0) { - throw new SagaException("idx shoud be less then ch size or more then 0"); - } return chapters.get(idx); } + public boolean isPresent(int idx) { + return idx >= 0 && idx < chapters.size(); + } public static Saga create() { return new Saga(); } - public enum Result{ - FINISHED,CANCELED, INPROCESS; + public enum Result { + FINISHED, ROLLBACK, CRASHED } - private static class Chapter { - String name; - int timeoutInSec; - public Chapter(String name, int timeoutInSec) { + public static class Chapter { + String name; + + public Chapter(String name) { this.name = name; - this.timeoutInSec = timeoutInSec; } + } } 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 154f97bab..71db1a7bf 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -24,22 +24,48 @@ package com.iluwatar.saga.orchestration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This pattern is used in distributed services to perform a group of operations atomically. + * This is an analog of transaction in a database but in terms of microservices architecture this is executed + * in a distributed environment + * + * A saga is a sequence of local transactions in a certain context. If one transaction fails for some reason, + * the saga executes compensating transactions(rollbacks) to undo the impact of the preceding transactions. + * + * In this approach, there is an orchestrator @see {@link SagaOrchestrator} that manages all the transactions and directs + * the participant services to execute local transactions based on events. + * + * @see Saga + * @see SagaOrchestrator + * @see Service + */ public class SagaApplication { + private static final Logger logger = LoggerFactory.getLogger(SagaApplication.class); + public static void main(String[] args) { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + Saga.Result goodOrder = sagaOrchestrator.execute("god_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 Saga createSaga() { - return Saga.create() - .chapter("init an order",10) - .chapter("booking a Fly",10) - .chapter("booking a Hotel",10) - .chapter("withdrawing Money",10); + 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 sd() { + private static ServiceDiscoveryService serviceDiscovery() { return new ServiceDiscoveryService() .discover(new OrderService()) diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java deleted file mode 100644 index 0055bb85e..000000000 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaException.java +++ /dev/null @@ -1,29 +0,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. - */ -package com.iluwatar.saga.orchestration; - -public class SagaException extends RuntimeException { - public SagaException(String message) { - super(message); - } -} 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 edc94bb22..34ad29b26 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -1,30 +1,139 @@ +/* + * 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.Objects; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Optional; + +import static com.iluwatar.saga.orchestration.Saga.Result.*; + +/** + * The orchestrator that manages all the transactions and directs + * the participant services to execute local transactions based on events. + */ public class SagaOrchestrator { - - private ExecutorService executor; - private AtomicBoolean isNormalFlow; - private AtomicBoolean isFinished; - private AtomicInteger currentState; + private static final Logger logger = LoggerFactory.getLogger(SagaOrchestrator.class); private final Saga saga; + private final ServiceDiscoveryService sd; + private final CurrentState state; - public SagaOrchestrator(Saga saga) { + + public SagaOrchestrator(Saga saga, ServiceDiscoveryService sd) { this.saga = saga; - this.isNormalFlow = new AtomicBoolean(true); - this.isFinished = new AtomicBoolean(false); - this.currentState = new AtomicInteger(0); - this.executor = Executors.newFixedThreadPool(20); + this.sd = sd; + this.state = new CurrentState(); } - public Saga.Result kickOff(K value) { + /** + * + * @param value incoming value + * @param type for incoming value + * @return result @see {@link Saga.Result} + */ + @SuppressWarnings("unchecked") + public Saga.Result execute(K value) { + state.cleanUp(); + logger.info(" The new saga is about to start"); + Saga.Result result = FINISHED; + K tempVal = value; + + while (true) { + int next = state.current(); + Saga.Chapter ch = saga.get(next); + Optional srvOpt = sd.find(ch.name); + + if (!srvOpt.isPresent()) { + state.directionToBack(); + state.back(); + continue; + } + + Chapter srv = srvOpt.get(); + + if (state.isForward()) { + ChapterResult processRes = srv.process(tempVal); + if (processRes.isSuccess()) { + next = state.forward(); + tempVal = (K) processRes.getValue(); + } else { + state.directionToBack(); + } + } else { + ChapterResult rlRes = srv.rollback(tempVal); + if (rlRes.isSuccess()) { + next = state.back(); + tempVal = (K) rlRes.getValue(); + } else { + result = CRASHED; + next = state.back(); + } + } + + + if (!saga.isPresent(next)) { + return state.isForward() ? FINISHED : result == CRASHED ? CRASHED : ROLLBACK; + } + } } + private static class CurrentState { + int currentNumber; + boolean isForward; + + void cleanUp() { + currentNumber = 0; + isForward = true; + } + + CurrentState() { + this.currentNumber = 0; + this.isForward = true; + } + + + boolean isForward() { + return isForward; + } + + void directionToBack() { + isForward = false; + } + + int forward() { + return ++currentNumber; + } + + int back() { + return --currentNumber; + } + + int current() { + return currentNumber; + } + } + } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java index 31a2ab702..20b34f55a 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java @@ -1,12 +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.orchestration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.iluwatar.saga.orchestration.Utility.sleepInSec; - +/** + * Common abstraction class representing services + * implementing a general contract @see {@link Chapter} + * @param type of incoming param + */ public abstract class Service implements Chapter { - private static final Logger logger = LoggerFactory.getLogger(Service.class); + protected static final Logger logger = LoggerFactory.getLogger(Service.class); @Override public abstract String getName(); @@ -14,18 +39,15 @@ public abstract class Service implements Chapter { @Override public ChapterResult process(K value) { - logger.info("The chapter about {} is starting", getName()); - logger.info("storing or calculating a data {} to db", value); - sleepInSec(1); - logger.info("the data {} has been stored or calculated successfully", value); + logger.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", + getName(),value); return ChapterResult.success(value); } @Override public ChapterResult rollback(K value) { - logger.info("Something is going wrong hence The chapter about {} is starting the rollback procedure", getName()); - sleepInSec(1); - logger.info("the data {} has been rollbacked successfully", value); + logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + getName(),value); return ChapterResult.success(value); } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java index 522f3f944..652740051 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java @@ -26,10 +26,13 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +/** + * The class representing a service discovery pattern. + */ public class ServiceDiscoveryService { private Map> services; - public Optional> find(String service) { + public Optional find(String service) { return Optional.ofNullable(services.getOrDefault(service, null)); } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java deleted file mode 100644 index bf5e902c0..000000000 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Utility.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iluwatar.saga.orchestration; - -public class Utility { - private Utility() { - } - - public static void sleepInSec(int sec){ - try { - Thread.sleep(sec*1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - -} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java index 25c7710dc..dad15cec3 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -1,5 +1,30 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.saga.orchestration; +/** + * Class representing a service to withdraw a money + */ public class WithdrawMoneyService extends Service { @Override public String getName() { @@ -8,7 +33,10 @@ public class WithdrawMoneyService extends Service { @Override public ChapterResult process(String value) { - if(value.equals("no-money")){ + if (value.equals("bad_order") || value.equals("crashed_order")) { + logger.info("The chapter '{}' has been started. But the exception has been raised." + + "The rollback is about to start", + getName(), value); return ChapterResult.failure(value); } return super.process(value); diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java new file mode 100644 index 000000000..6950158aa --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java @@ -0,0 +1,13 @@ +package com.iluwatar.saga.orchestration; + +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/orchestration/SagaOrchestratorInternallyTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java new file mode 100644 index 000000000..b8377cebf --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java @@ -0,0 +1,117 @@ +package com.iluwatar.saga.orchestration; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +public class SagaOrchestratorInternallyTest { + + private List records = new ArrayList<>(); + + @Test + public void execute() { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + Saga.Result result = sagaOrchestrator.execute(1); + Assert.assertEquals(result, Saga.Result.ROLLBACK); + Assert.assertArrayEquals( + records.toArray(new String[]{}), + new String[]{"+1","+2","+3","+4","-4","-3","-2","-1"}); + } + + private static Saga newSaga() { + return Saga + .create() + .chapter("1") + .chapter("2") + .chapter("3") + .chapter("4"); + } + + private ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new Service1()) + .discover(new Service2()) + .discover(new Service3()) + .discover(new Service4()); + } + + class Service1 extends Service { + + @Override + public String getName() { + return "1"; + } + + @Override + public ChapterResult process(Integer value) { + records.add("+1"); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-1"); + return ChapterResult.success(value); + } + } + + class Service2 extends Service { + + @Override + public String getName() { + return "2"; + } + @Override + public ChapterResult process(Integer value) { + records.add("+2"); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-2"); + return ChapterResult.success(value); + } + } + + class Service3 extends Service { + + @Override + public String getName() { + return "3"; + } + @Override + public ChapterResult process(Integer value) { + records.add("+3"); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-3"); + return ChapterResult.success(value); + } + } + + class Service4 extends Service { + + @Override + public String getName() { + return "4"; + } + @Override + public ChapterResult process(Integer value) { + records.add("+4"); + return ChapterResult.failure(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-4"); + return ChapterResult.success(value); + } + } +} \ No newline at end of file diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java new file mode 100644 index 000000000..8558b1f5a --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java @@ -0,0 +1,37 @@ +package com.iluwatar.saga.orchestration; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SagaOrchestratorTest { + + @Test + public void execute() { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + Saga.Result badOrder = sagaOrchestrator.execute("bad_order"); + Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order"); + + Assert.assertEquals(badOrder, Saga.Result.ROLLBACK); + Assert.assertEquals(crashedOrder, Saga.Result.CRASHED); + } + + private static Saga newSaga() { + return Saga + .create() + .chapter("init an order") + .chapter("booking a Fly") + .chapter("booking a Hotel") + .chapter("withdrawing Money"); + } + + private static ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new OrderService()) + .discover(new FlyBookingService()) + .discover(new HotelBookingService()) + .discover(new WithdrawMoneyService()); + } +} \ No newline at end of file From 1b32e23493d1383033603162741c42264c8c965d Mon Sep 17 00:00:00 2001 From: Besok Date: Sun, 3 Nov 2019 12:03:33 +0000 Subject: [PATCH 13/19] add ch --- .../ServiceDiscoveryService.java | 4 +- .../iluwatar/saga/choreography/Chapter.java | 59 +++++++++++++++++++ .../choreography/ChoreographyService.java | 33 +++++++++++ .../iluwatar/saga/choreography/RichSaga.java | 4 ++ .../saga/orchestration/FlyBookingService.java | 2 +- .../orchestration/HotelBookingService.java | 2 +- ...Service.java => OrchestrationService.java} | 4 +- .../saga/orchestration/OrderService.java | 2 +- .../com/iluwatar/saga/orchestration/Saga.java | 3 + .../saga/orchestration/SagaApplication.java | 3 +- .../saga/orchestration/SagaOrchestrator.java | 1 + .../orchestration/WithdrawMoneyService.java | 2 +- .../SagaOrchestratorInternallyTest.java | 9 +-- .../orchestration/SagaOrchestratorTest.java | 3 +- 14 files changed, 117 insertions(+), 14 deletions(-) rename saga/src/main/java/com/iluwatar/saga/{orchestration => }/ServiceDiscoveryService.java (95%) create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java rename saga/src/main/java/com/iluwatar/saga/orchestration/{Service.java => OrchestrationService.java} (95%) diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/ServiceDiscoveryService.java similarity index 95% rename from saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java rename to saga/src/main/java/com/iluwatar/saga/ServiceDiscoveryService.java index 652740051..f9d91b53e 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/ServiceDiscoveryService.java @@ -20,7 +20,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.saga.orchestration; +package com.iluwatar.saga; + +import com.iluwatar.saga.orchestration.Chapter; import java.util.HashMap; import java.util.Map; diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java new file mode 100644 index 000000000..a76e2c4df --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.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 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 { + + /** + * @return service name. + */ + String getName(); + + /** + * every chapter is responsible for a part of saga. + * @param value incoming value + * @return saga result @see {@link RichSaga} + */ + RichSaga execute(K value); + + /** + * The operation executed in general case. + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult process(K value); + + /** + * The operation executed in rollback case. + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult rollback(K value); + +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java new file mode 100644 index 000000000..e88a131b2 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java @@ -0,0 +1,33 @@ +package com.iluwatar.saga.choreography; + +import com.iluwatar.saga.ServiceDiscoveryService; +import com.iluwatar.saga.orchestration.Chapter; +import com.iluwatar.saga.orchestration.ChapterResult; +import com.iluwatar.saga.orchestration.OrchestrationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class ChoreographyService implements Chapter { + + protected static final Logger logger = LoggerFactory.getLogger(OrchestrationService.class); + + private final ServiceDiscoveryService service; + protected ChoreographyService(ServiceDiscoveryService service) { + this.service = service; + } + + @Override + public ChapterResult process(K value) { + logger.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", + getName(),value); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(K value) { + logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + getName(),value); + return ChapterResult.success(value); + } + +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java b/saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java new file mode 100644 index 000000000..27e099fc7 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java @@ -0,0 +1,4 @@ +package com.iluwatar.saga.choreography; + +public class RichSaga { +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java index 6cb8479c0..34f3f4f18 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to book a fly */ -public class FlyBookingService extends Service { +public class FlyBookingService extends OrchestrationService { @Override public String getName() { return "booking a Fly"; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java index e21046328..a4da64d36 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to book a hotel */ -public class HotelBookingService extends Service { +public class HotelBookingService extends OrchestrationService { @Override public String getName() { return "booking a Hotel"; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationService.java similarity index 95% rename from saga/src/main/java/com/iluwatar/saga/orchestration/Service.java rename to saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationService.java index 20b34f55a..7f0e9a03c 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationService.java @@ -30,8 +30,8 @@ import org.slf4j.LoggerFactory; * implementing a general contract @see {@link Chapter} * @param type of incoming param */ -public abstract class Service implements Chapter { - protected static final Logger logger = LoggerFactory.getLogger(Service.class); +public abstract class OrchestrationService implements Chapter { + protected static final Logger logger = LoggerFactory.getLogger(OrchestrationService.class); @Override public abstract String getName(); diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java index 6edd94ace..8fb7b118d 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to init a new order. */ -public class OrderService extends Service { +public class OrderService extends OrchestrationService { @Override public String getName() { return "init an order"; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java index c21435f20..9f8312d9f 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -70,5 +70,8 @@ public class Saga { this.name = name; } + public String getName() { + return name; + } } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java index 71db1a7bf..b69d04013 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -24,6 +24,7 @@ package com.iluwatar.saga.orchestration; +import com.iluwatar.saga.ServiceDiscoveryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +41,7 @@ import org.slf4j.LoggerFactory; * * @see Saga * @see SagaOrchestrator - * @see Service + * @see OrchestrationService */ public class SagaApplication { private static final Logger logger = LoggerFactory.getLogger(SagaApplication.class); 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 34ad29b26..588a860aa 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -22,6 +22,7 @@ */ package com.iluwatar.saga.orchestration; +import com.iluwatar.saga.ServiceDiscoveryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java index dad15cec3..d53f106cb 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to withdraw a money */ -public class WithdrawMoneyService extends Service { +public class WithdrawMoneyService extends OrchestrationService { @Override public String getName() { return "withdrawing Money"; 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 b8377cebf..a40a3ebec 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java @@ -1,5 +1,6 @@ package com.iluwatar.saga.orchestration; +import com.iluwatar.saga.ServiceDiscoveryService; import org.junit.Assert; import org.junit.Test; @@ -38,7 +39,7 @@ public class SagaOrchestratorInternallyTest { .discover(new Service4()); } - class Service1 extends Service { + class Service1 extends OrchestrationService { @Override public String getName() { @@ -58,7 +59,7 @@ public class SagaOrchestratorInternallyTest { } } - class Service2 extends Service { + class Service2 extends OrchestrationService { @Override public String getName() { @@ -77,7 +78,7 @@ public class SagaOrchestratorInternallyTest { } } - class Service3 extends Service { + class Service3 extends OrchestrationService { @Override public String getName() { @@ -96,7 +97,7 @@ public class SagaOrchestratorInternallyTest { } } - class Service4 extends Service { + class Service4 extends OrchestrationService { @Override public String getName() { 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 8558b1f5a..cd659601e 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java @@ -1,10 +1,9 @@ package com.iluwatar.saga.orchestration; +import com.iluwatar.saga.ServiceDiscoveryService; import org.junit.Assert; import org.junit.Test; -import static org.junit.Assert.*; - public class SagaOrchestratorTest { @Test From ffeee2f1a48f1fd041f3a327814a31efe99c9434 Mon Sep 17 00:00:00 2001 From: Besok Date: Sun, 3 Nov 2019 12:35:01 +0000 Subject: [PATCH 14/19] separate pkgs --- .../iluwatar/saga/choreography/Chapter.java | 59 ------------------- .../choreography/ChoreographyService.java | 33 ----------- .../iluwatar/saga/choreography/RichSaga.java | 4 -- .../saga/orchestration/FlyBookingService.java | 2 +- .../orchestration/HotelBookingService.java | 2 +- .../saga/orchestration/OrderService.java | 2 +- .../saga/orchestration/SagaApplication.java | 2 +- ...OrchestrationService.java => Service.java} | 4 +- .../orchestration/WithdrawMoneyService.java | 2 +- .../SagaOrchestratorInternallyTest.java | 8 +-- 10 files changed, 11 insertions(+), 107 deletions(-) delete mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java delete mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java delete mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java rename saga/src/main/java/com/iluwatar/saga/orchestration/{OrchestrationService.java => Service.java} (95%) diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java deleted file mode 100644 index a76e2c4df..000000000 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java +++ /dev/null @@ -1,59 +0,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. - */ -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 { - - /** - * @return service name. - */ - String getName(); - - /** - * every chapter is responsible for a part of saga. - * @param value incoming value - * @return saga result @see {@link RichSaga} - */ - RichSaga execute(K value); - - /** - * The operation executed in general case. - * @param value incoming value - * @return result {@link ChapterResult} - */ - ChapterResult process(K value); - - /** - * The operation executed in rollback case. - * @param value incoming value - * @return result {@link ChapterResult} - */ - ChapterResult rollback(K value); - -} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java deleted file mode 100644 index e88a131b2..000000000 --- a/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyService.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iluwatar.saga.choreography; - -import com.iluwatar.saga.ServiceDiscoveryService; -import com.iluwatar.saga.orchestration.Chapter; -import com.iluwatar.saga.orchestration.ChapterResult; -import com.iluwatar.saga.orchestration.OrchestrationService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class ChoreographyService implements Chapter { - - protected static final Logger logger = LoggerFactory.getLogger(OrchestrationService.class); - - private final ServiceDiscoveryService service; - protected ChoreographyService(ServiceDiscoveryService service) { - this.service = service; - } - - @Override - public ChapterResult process(K value) { - logger.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", - getName(),value); - return ChapterResult.success(value); - } - - @Override - public ChapterResult rollback(K value) { - logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", - getName(),value); - return ChapterResult.success(value); - } - -} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java b/saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java deleted file mode 100644 index 27e099fc7..000000000 --- a/saga/src/main/java/com/iluwatar/saga/choreography/RichSaga.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.iluwatar.saga.choreography; - -public class RichSaga { -} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java index 34f3f4f18..6cb8479c0 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to book a fly */ -public class FlyBookingService extends OrchestrationService { +public class FlyBookingService extends Service { @Override public String getName() { return "booking a Fly"; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java index a4da64d36..e21046328 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to book a hotel */ -public class HotelBookingService extends OrchestrationService { +public class HotelBookingService extends Service { @Override public String getName() { return "booking a Hotel"; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java index 8fb7b118d..6edd94ace 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to init a new order. */ -public class OrderService extends OrchestrationService { +public class OrderService extends Service { @Override public String getName() { return "init an order"; 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 b69d04013..013d9a5f0 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -41,7 +41,7 @@ import org.slf4j.LoggerFactory; * * @see Saga * @see SagaOrchestrator - * @see OrchestrationService + * @see Service */ public class SagaApplication { private static final Logger logger = LoggerFactory.getLogger(SagaApplication.class); diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java similarity index 95% rename from saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationService.java rename to saga/src/main/java/com/iluwatar/saga/orchestration/Service.java index 7f0e9a03c..20b34f55a 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java @@ -30,8 +30,8 @@ import org.slf4j.LoggerFactory; * implementing a general contract @see {@link Chapter} * @param type of incoming param */ -public abstract class OrchestrationService implements Chapter { - protected static final Logger logger = LoggerFactory.getLogger(OrchestrationService.class); +public abstract class Service implements Chapter { + protected static final Logger logger = LoggerFactory.getLogger(Service.class); @Override public abstract String getName(); diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java index d53f106cb..dad15cec3 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -25,7 +25,7 @@ package com.iluwatar.saga.orchestration; /** * Class representing a service to withdraw a money */ -public class WithdrawMoneyService extends OrchestrationService { +public class WithdrawMoneyService extends Service { @Override public String getName() { return "withdrawing Money"; 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 a40a3ebec..16c54c920 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java @@ -39,7 +39,7 @@ public class SagaOrchestratorInternallyTest { .discover(new Service4()); } - class Service1 extends OrchestrationService { + class Service1 extends Service { @Override public String getName() { @@ -59,7 +59,7 @@ public class SagaOrchestratorInternallyTest { } } - class Service2 extends OrchestrationService { + class Service2 extends Service { @Override public String getName() { @@ -78,7 +78,7 @@ public class SagaOrchestratorInternallyTest { } } - class Service3 extends OrchestrationService { + class Service3 extends Service { @Override public String getName() { @@ -97,7 +97,7 @@ public class SagaOrchestratorInternallyTest { } } - class Service4 extends OrchestrationService { + class Service4 extends Service { @Override public String getName() { From fbcfeb072a588ff6dff203c447674b40c61dccc3 Mon Sep 17 00:00:00 2001 From: Besok Date: Sun, 3 Nov 2019 12:52:38 +0000 Subject: [PATCH 15/19] add info --- .../iluwatar/saga/choreography/Chapter.java | 53 +++++++++++++++++++ .../com/iluwatar/saga/choreography/Saga.java | 34 ++++++++++++ .../com/iluwatar/saga/orchestration/Saga.java | 2 +- 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/Saga.java diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java new file mode 100644 index 000000000..f610b9f62 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.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; + +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 { + + /** + * @return service name. + */ + String getName(); + + /** + * The operation executed in general case. + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult process(K value); + + /** + * The operation executed in rollback case. + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult rollback(K value); + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java new file mode 100644 index 000000000..94c8a119d --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.saga.choreography; + +/** + * Saga representation. + * Saga consists of chapters. + * Every Chapter is executed a certain service. + */ +public class 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 9f8312d9f..d6a703706 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -27,7 +27,7 @@ import java.util.List; /** * Saga representation. - * Saca consists of chapters. + * Saga consists of chapters. * Every Chapter is executed a certain service. */ public class Saga { From 09b4663b9a15c406b899bc6900320a8361c92253 Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 9 Nov 2019 17:42:49 +0000 Subject: [PATCH 16/19] add choreogr --- .../iluwatar/saga/choreography/Chapter.java | 27 ++-- .../saga/choreography/FlyBookingService.java | 38 ++++++ .../choreography/HotelBookingService.java | 40 ++++++ .../saga/choreography/OrderService.java | 39 ++++++ .../com/iluwatar/saga/choreography/Saga.java | 125 ++++++++++++++++++ .../saga/choreography/SagaApplication.java | 77 +++++++++++ .../iluwatar/saga/choreography/Service.java | 103 +++++++++++++++ .../choreography/ServiceDiscoveryService.java | 60 +++++++++ .../choreography/WithdrawMoneyService.java | 53 ++++++++ .../com/iluwatar/saga/orchestration/Saga.java | 3 +- .../saga/orchestration/SagaApplication.java | 5 +- .../saga/orchestration/SagaOrchestrator.java | 2 +- .../ServiceDiscoveryService.java | 4 +- .../choreography/SagaApplicationTest.java | 35 +++++ .../choreography/SagaChoreographyTest.java | 59 +++++++++ .../orchestration/SagaApplicationTest.java | 22 +++ .../SagaOrchestratorInternallyTest.java | 25 +++- .../orchestration/SagaOrchestratorTest.java | 23 +++- 18 files changed, 719 insertions(+), 21 deletions(-) create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/Service.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java rename saga/src/main/java/com/iluwatar/saga/{ => orchestration}/ServiceDiscoveryService.java (95%) create mode 100644 saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java create mode 100644 saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java 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; From 74c7273381a9c2fb521f1aa3b2924d9d290c876b Mon Sep 17 00:00:00 2001 From: Besok Date: Sat, 9 Nov 2019 18:57:31 +0000 Subject: [PATCH 17/19] rem space --- .../src/main/java/com/iluwatar/roleobject/InvestorRole.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java index 6d5c17c90..d6ec6bc42 100644 --- a/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java +++ b/role-object/src/main/java/com/iluwatar/roleobject/InvestorRole.java @@ -43,6 +43,6 @@ public class InvestorRole extends CustomerRole { } public String invest() { - return String.format("Investor %s has invested %d dollars", name, amountToInvest); + return String.format("Investor %s has invested %d dollars", name, amountToInvest); } } From de56cbb971207e7d754e0d86038e5ac9f998ea79 Mon Sep 17 00:00:00 2001 From: Besok Date: Sun, 10 Nov 2019 13:09:41 +0000 Subject: [PATCH 18/19] change according to cgeckstyle --- saga/README.md | 4 +- saga/pom.xml | 33 +-- ...{Chapter.java => ChoreographyChapter.java} | 53 ++-- .../saga/choreography/FlyBookingService.java | 14 +- .../choreography/HotelBookingService.java | 16 +- .../saga/choreography/OrderService.java | 16 +- .../com/iluwatar/saga/choreography/Saga.java | 266 +++++++++++------- .../saga/choreography/SagaApplication.java | 53 ++-- .../iluwatar/saga/choreography/Service.java | 132 ++++----- .../choreography/ServiceDiscoveryService.java | 39 +-- .../choreography/WithdrawMoneyService.java | 36 +-- .../saga/orchestration/ChapterResult.java | 46 +-- .../saga/orchestration/FlyBookingService.java | 8 +- .../orchestration/HotelBookingService.java | 36 +-- ...Chapter.java => OrchestrationChapter.java} | 39 +-- .../saga/orchestration/OrderService.java | 8 +- .../com/iluwatar/saga/orchestration/Saga.java | 83 +++--- .../saga/orchestration/SagaApplication.java | 58 ++-- .../saga/orchestration/SagaOrchestrator.java | 187 ++++++------ .../iluwatar/saga/orchestration/Service.java | 35 +-- .../ServiceDiscoveryService.java | 22 +- .../orchestration/WithdrawMoneyService.java | 26 +- .../choreography/SagaApplicationTest.java | 12 +- .../choreography/SagaChoreographyTest.java | 53 ++-- .../orchestration/SagaApplicationTest.java | 11 +- .../SagaOrchestratorInternallyTest.java | 186 ++++++------ .../orchestration/SagaOrchestratorTest.java | 51 ++-- 27 files changed, 817 insertions(+), 706 deletions(-) rename saga/src/main/java/com/iluwatar/saga/choreography/{Chapter.java => ChoreographyChapter.java} (65%) rename saga/src/main/java/com/iluwatar/saga/orchestration/{Chapter.java => OrchestrationChapter.java} (69%) diff --git a/saga/README.md b/saga/README.md index 276f12ee1..546ad598b 100644 --- a/saga/README.md +++ b/saga/README.md @@ -1,7 +1,7 @@ --- layout: pattern title: Saga -folder: Communication +folder: saga permalink: /patterns/saga/ categories: Behavioral tags: @@ -43,4 +43,4 @@ Use the Saga pattern, if: - you can not use 2PC(two phase commit) ## Credits -- [pattern description](https://microservices.io/patterns/data/saga.html) \ No newline at end of file +- [Pattern: Saga](https://microservices.io/patterns/data/saga.html) \ No newline at end of file diff --git a/saga/pom.xml b/saga/pom.xml index 26c331237..111f4e7b8 100644 --- a/saga/pom.xml +++ b/saga/pom.xml @@ -23,22 +23,23 @@ THE SOFTWARE. --> - - 4.0.0 - - com.iluwatar - java-design-patterns - 1.22.0-SNAPSHOT - + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.22.0-SNAPSHOT + - saga - - - junit - junit - test - - + saga + + + junit + junit + test + + diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java similarity index 65% rename from saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java rename to saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java index 51b0ce6b8..caa377a1e 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Chapter.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java @@ -24,37 +24,40 @@ package com.iluwatar.saga.choreography; /** - * Chapter is an interface representing a contract for an external service. + * 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 Chapter { + */ +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); + /** + * 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. - */ - String getName(); + /** + * @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 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); + /** + * The operation executed in rollback case. + * + * @param saga incoming saga + * @return result {@link Saga} + */ + 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 index 591324c52..60b183be3 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java @@ -27,12 +27,12 @@ package com.iluwatar.saga.choreography; * Class representing a service to book a fly */ public class FlyBookingService extends Service { - public FlyBookingService(ServiceDiscoveryService service) { - super(service); - } + public FlyBookingService(ServiceDiscoveryService service) { + super(service); + } - @Override - public String getName() { - return "booking a Fly"; - } + @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 index e822a3568..0bbf1d3e3 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java @@ -27,14 +27,14 @@ package com.iluwatar.saga.choreography; * Class representing a service to book a hotel */ public class HotelBookingService extends Service { - public HotelBookingService(ServiceDiscoveryService service) { - super(service); - } + public HotelBookingService(ServiceDiscoveryService service) { + super(service); + } - @Override - public String getName() { - return "booking a Hotel"; - } + @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 index 6c8b9912e..3a2c002b1 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java @@ -26,14 +26,14 @@ package com.iluwatar.saga.choreography; /** * Class representing a service to init a new order. */ -public class OrderService extends Service{ +public class OrderService extends Service { - public OrderService(ServiceDiscoveryService service) { - super(service); - } + public OrderService(ServiceDiscoveryService service) { + super(service); + } - @Override - public String getName() { - return "init an order"; - } + @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 34a8d6a0f..35069b80f 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java @@ -29,131 +29,187 @@ import java.util.List; /** * Saga representation. * Saga consists of chapters. - * Every Chapter is executed a certain service. + * Every ChoreographyChapter is executed a certain service. */ public class Saga { - private List chapters; - private int pos; - private boolean forward; - private boolean finished; + 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); + 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; } - void setFinished(boolean finished) { - this.finished = finished; - } - boolean isForward() { - return forward; - } - int forward() { - return ++pos; - } + return SagaResult.PROGRESS; + } - int back() { - this.forward = false; - return --pos; + /** + * 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; + } - public Saga() { - this.chapters = new ArrayList<>(); - this.pos = 0; - this.forward = true; - this.finished = false; - } + private Saga() { + this.chapters = new ArrayList<>(); + this.pos = 0; + this.forward = true; + this.finished = false; + } - Chapter getCurrent() { - return chapters.get(pos); - } + Chapter getCurrent() { + return chapters.get(pos); + } - boolean isPresent() { - return pos >= 0 && pos < chapters.size(); - } - boolean isCurrentSuccess(){ - return chapters.get(pos).isSuccess(); + 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; } - /*** - * Class presents a chapter status and incoming parameters(incoming parameter transforms to outcoming parameter) + 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 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 void setResult(ChapterResult result) { + this.result = result; } - - public enum ChapterResult { - INIT, SUCCESS, ROLLBACK + /** + * the result for chapter is good + * @return true if is good otherwise bad + */ + public boolean isSuccess() { + return result == ChapterResult.SUCCESS; } + } - public enum SagaResult { - PROGRESS, FINISHED, ROLLBACKED - } - @Override - public String toString() { - return "Saga{" + - "chapters=" + Arrays.toString(chapters.toArray()) + - ", pos=" + pos + - ", forward=" + forward + - '}'; - } + /** + * result for chapter + */ + public enum ChapterResult { + INIT, SUCCESS, ROLLBACK + } + + /** + * result for saga + */ + public enum SagaResult { + PROGRESS, FINISHED, ROLLBACKED + } + + @Override + public String toString() { + return "Saga{" + + "chapters=" + + Arrays.toString(chapters.toArray()) + + ", pos=" + + pos + + ", forward=" + + forward + + '}'; + } } diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java index 46792adac..3aeb8672c 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java @@ -44,34 +44,37 @@ import org.slf4j.LoggerFactory; * @see Service */ public class SagaApplication { - private static final Logger logger = LoggerFactory.getLogger(SagaApplication.class); + 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()); + /** + * 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 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)); - } + 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 index 0f182e570..a602b1147 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Service.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java @@ -25,79 +25,85 @@ package com.iluwatar.saga.choreography; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.function.Supplier; + /** * Common abstraction class representing services - * implementing a general contract @see {@link Chapter} + * implementing a general contract @see {@link ChoreographyChapter} */ -public abstract class Service implements Chapter { - protected static final Logger logger = LoggerFactory.getLogger(Service.class); +public abstract class Service implements ChoreographyChapter { + protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class); - private final ServiceDiscoveryService sd; + private final ServiceDiscoveryService sd; - public Service(ServiceDiscoveryService service) { - this.sd = service; - } + 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); + @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(); } - Saga finalNextSaga = nextSaga; + } else { + nextSaga = rollback(saga); + nextVal = nextSaga.getCurrentValue(); + nextSaga.back(); + } - return sd.find(chapterName).map(ch -> ch.execute(finalNextSaga)) - .orElseThrow(RuntimeException::new); + if (isSagaFinished(nextSaga)) { + return nextSaga; + } + + nextSaga.setCurrentValue(nextVal); } + Saga finalNextSaga = nextSaga; - @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; + return sd.find(chapterName).map(ch -> ch.execute(finalNextSaga)) + .orElseThrow(serviceNotFoundException(chapterName)); + } + + private Supplier serviceNotFoundException(String chServiceName) { + return () -> new RuntimeException(String.format("the service %s has not been found", chServiceName)); + } + + @Override + public Saga process(Saga saga) { + Object inValue = saga.getCurrentValue(); + LOGGER.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", + getName(), inValue); + saga.setCurrentStatus(Saga.ChapterResult.SUCCESS); + saga.setCurrentValue(inValue); + return saga; + } + + @Override + public Saga rollback(Saga saga) { + Object inValue = saga.getCurrentValue(); + LOGGER.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + getName(), inValue); + + saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK); + saga.setCurrentValue(inValue); + return saga; + } + + private boolean isSagaFinished(Saga saga) { + if (!saga.isPresent()) { + saga.setFinished(true); + LOGGER.info(" the saga has been finished with {} status", saga.getResult()); + return true; } + return false; + } } diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java index 11512d112..919c99f02 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java @@ -32,29 +32,30 @@ import java.util.Optional; * The class representing a service discovery pattern. */ public class ServiceDiscoveryService { - private Map services; + private Map services; - /** - * find any service - * @return any service - * @throws NoSuchElementException if no elements further - */ - public Chapter findAny(){ - return services.values().iterator().next(); - } + /** + * find any service + * + * @return any service + * @throws NoSuchElementException if no elements further + */ + public ChoreographyChapter findAny() { + return services.values().iterator().next(); + } - public Optional find(String service) { - return Optional.ofNullable(services.getOrDefault(service, null)); - } + public 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 discover(ChoreographyChapter chapterService) { + services.put(chapterService.getName(), chapterService); + return this; + } - public ServiceDiscoveryService() { - this.services = new HashMap<>(); - } + 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 index c13b1b1f8..b30a05e95 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java @@ -28,26 +28,26 @@ package com.iluwatar.saga.choreography; */ public class WithdrawMoneyService extends Service { - public WithdrawMoneyService(ServiceDiscoveryService service) { - super(service); - } + public WithdrawMoneyService(ServiceDiscoveryService service) { + super(service); + } - @Override - public String getName() { - return "withdrawing Money"; - } + @Override + public String getName() { + return "withdrawing Money"; + } - @Override - public Saga process(Saga saga) { - Object inValue = saga.getCurrentValue(); + @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); + if (inValue.equals("bad_order")) { + LOGGER.info("The chapter '{}' has been started. But the exception has been raised." + + "The rollback is about to start", + getName(), inValue); + saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK); + return saga; } + return super.process(saga); + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java index eae95b339..a8a1e7b5e 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java @@ -24,34 +24,38 @@ package com.iluwatar.saga.orchestration; /** * Executing result for chapter + * * @param incoming value */ public class ChapterResult { - private K value; - private State state; + private K value; + private State state; - public K getValue() { - return value; - } + public K getValue() { + return value; + } - ChapterResult(K value, State state) { - this.value = value; - this.state = state; - } + ChapterResult(K value, State state) { + this.value = value; + this.state = state; + } - public boolean isSuccess(){ - return state == State.SUCCESS; - } + public boolean isSuccess() { + return state == State.SUCCESS; + } - public static ChapterResult success(K val) { - return new ChapterResult<>(val, State.SUCCESS); - } + public static ChapterResult success(K val) { + return new ChapterResult<>(val, State.SUCCESS); + } - public static ChapterResult failure(K val) { - return new ChapterResult<>(val, State.FAILURE); - } + public static ChapterResult failure(K val) { + return new ChapterResult<>(val, State.FAILURE); + } - public enum State { - SUCCESS, FAILURE - } + /** + * state for chapter + */ + public enum State { + SUCCESS, FAILURE + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java index 6cb8479c0..5dc0577bc 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -26,8 +26,8 @@ package com.iluwatar.saga.orchestration; * Class representing a service to book a fly */ public class FlyBookingService extends Service { - @Override - public String getName() { - return "booking a Fly"; - } + @Override + public String getName() { + return "booking a Fly"; + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java index e21046328..51d8d5aad 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -26,25 +26,25 @@ package com.iluwatar.saga.orchestration; * Class representing a service to book a hotel */ public class HotelBookingService extends Service { - @Override - public String getName() { - return "booking a Hotel"; + @Override + public String getName() { + return "booking a Hotel"; + } + + + @Override + public ChapterResult rollback(String value) { + if (value.equals("crashed_order")) { + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been failed.The saga has been crashed.", + getName(), value); + + return ChapterResult.failure(value); } + LOGGER.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + getName(), value); - @Override - public ChapterResult rollback(String value) { - if(value.equals("crashed_order")){ - logger.info("The Rollback for a chapter '{}' has been started. " + - "The data {} has been failed.The saga has been crashed.", - getName(), value); - - return ChapterResult.failure(value); - } - - logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", - getName(), value); - - return super.rollback(value); - } + return super.rollback(value); + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java similarity index 69% rename from saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java rename to saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java index 961a88937..861382a00 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Chapter.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java @@ -23,28 +23,31 @@ package com.iluwatar.saga.orchestration; /** - * Chapter is an interface representing a contract for an external service. + * ChoreographyChapter is an interface representing a contract for an external service. + * * @param is type for passing params */ -public interface Chapter { +public interface OrchestrationChapter { - /** - * @return service name. - */ - String getName(); + /** + * @return service name. + */ + String getName(); - /** - * The operation executed in general case. - * @param value incoming value - * @return result {@link ChapterResult} - */ - ChapterResult process(K value); + /** + * The operation executed in general case. + * + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult process(K value); - /** - * The operation executed in rollback case. - * @param value incoming value - * @return result {@link ChapterResult} - */ - ChapterResult rollback(K value); + /** + * The operation executed in rollback case. + * + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult rollback(K value); } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java index 6edd94ace..32b9ce90b 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -26,8 +26,8 @@ package com.iluwatar.saga.orchestration; * Class representing a service to init a new order. */ public class OrderService extends Service { - @Override - public String getName() { - return "init an order"; - } + @Override + public String getName() { + return "init an order"; + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java index f24a8366c..e42b06f9a 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -28,49 +28,56 @@ import java.util.List; /** * Saga representation. * Saga consists of chapters. - * Every Chapter is executed by a certain service. + * Every ChoreographyChapter is executed by a certain service. */ public class Saga { - private List chapters; + private List chapters; - public Saga() { - this.chapters = new ArrayList<>(); + + 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 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(); - } - - public enum Result { - FINISHED, ROLLBACK, CRASHED - } - - public static class Chapter { - String name; - - public Chapter(String name) { - this.name = name; - } - - public String getName() { - return name; - } + public String getName() { + return name; } + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java index bbeca62bb..6d7e9fd8d 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -31,11 +31,12 @@ import org.slf4j.LoggerFactory; * This pattern is used in distributed services to perform a group of operations atomically. * This is an analog of transaction in a database but in terms of microservices architecture this is executed * in a distributed environment - * + *

* A saga is a sequence of local transactions in a certain context. If one transaction fails for some reason, * the saga executes compensating transactions(rollbacks) to undo the impact of the preceding transactions. - * - * In this approach, there is an orchestrator @see {@link SagaOrchestrator} that manages all the transactions and directs + *

+ * 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) @@ -45,34 +46,37 @@ import org.slf4j.LoggerFactory; * @see Service */ public class SagaApplication { - private static final Logger logger = LoggerFactory.getLogger(SagaApplication.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class); - public static void main(String[] args) { - SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + /** + * 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"); + 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); - } + 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 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()); - } + private static ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new OrderService()) + .discover(new FlyBookingService()) + .discover(new HotelBookingService()) + .discover(new WithdrawMoneyService()); + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java index c005d70df..2f25ebd46 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -34,107 +34,112 @@ import static com.iluwatar.saga.orchestration.Saga.Result.*; * 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; + private static final Logger LOGGER = LoggerFactory.getLogger(SagaOrchestrator.class); + private final Saga saga; + private final ServiceDiscoveryService sd; + private final CurrentState state; - public SagaOrchestrator(Saga saga, ServiceDiscoveryService sd) { - this.saga = saga; - this.sd = sd; - this.state = new CurrentState(); + /** + * Create a new service to orchetrate sagas + * @param saga saga to process + * @param sd service discovery @see {@link ServiceDiscoveryService} + */ + public SagaOrchestrator(Saga saga, ServiceDiscoveryService sd) { + this.saga = saga; + this.sd = sd; + this.state = new CurrentState(); + } + + /** + * pipeline to execute saga process/story + * + * @param value incoming value + * @param type for incoming value + * @return result @see {@link Saga.Result} + */ + @SuppressWarnings("unchecked") + public Saga.Result execute(K value) { + state.cleanUp(); + LOGGER.info(" The new saga is about to start"); + Saga.Result result = FINISHED; + K tempVal = value; + + while (true) { + int next = state.current(); + Saga.Chapter ch = saga.get(next); + Optional srvOpt = sd.find(ch.name); + + if (!srvOpt.isPresent()) { + state.directionToBack(); + state.back(); + continue; + } + + OrchestrationChapter srv = srvOpt.get(); + + if (state.isForward()) { + ChapterResult processRes = srv.process(tempVal); + if (processRes.isSuccess()) { + next = state.forward(); + tempVal = (K) processRes.getValue(); + } else { + state.directionToBack(); + } + } else { + ChapterResult rlRes = srv.rollback(tempVal); + if (rlRes.isSuccess()) { + next = state.back(); + tempVal = (K) rlRes.getValue(); + } else { + result = CRASHED; + next = state.back(); + } + } + + + if (!saga.isPresent(next)) { + return state.isForward() ? FINISHED : result == CRASHED ? CRASHED : ROLLBACK; + } } - /** - * pipeline to execute saga process/story - * - * @param value incoming value - * @param type for incoming value - * @return result @see {@link Saga.Result} - */ - @SuppressWarnings("unchecked") - public Saga.Result execute(K value) { - state.cleanUp(); - logger.info(" The new saga is about to start"); - Saga.Result result = FINISHED; - K tempVal = value; - - while (true) { - int next = state.current(); - Saga.Chapter ch = saga.get(next); - Optional srvOpt = sd.find(ch.name); - - if (!srvOpt.isPresent()) { - state.directionToBack(); - state.back(); - continue; - } - - Chapter 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; } - 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; - } + boolean isForward() { + return isForward; } + void directionToBack() { + isForward = false; + } + + int forward() { + return ++currentNumber; + } + + int back() { + return --currentNumber; + } + + int current() { + return currentNumber; + } + } + } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java index 20b34f55a..c4ba2a9e1 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java @@ -27,29 +27,30 @@ import org.slf4j.LoggerFactory; /** * Common abstraction class representing services - * implementing a general contract @see {@link Chapter} + * implementing a general contract @see {@link OrchestrationChapter} + * * @param type of incoming param */ -public abstract class Service implements Chapter { - protected static final Logger logger = LoggerFactory.getLogger(Service.class); +public abstract class Service implements OrchestrationChapter { + protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class); - @Override - public abstract String getName(); + @Override + public abstract String getName(); - @Override - public ChapterResult process(K value) { - logger.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", - getName(),value); - return ChapterResult.success(value); - } + @Override + public ChapterResult process(K value) { + LOGGER.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", + getName(), value); + return ChapterResult.success(value); + } - @Override - public ChapterResult rollback(K value) { - logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", - getName(),value); - return ChapterResult.success(value); - } + @Override + public ChapterResult rollback(K value) { + LOGGER.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + getName(), value); + return ChapterResult.success(value); + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java index 652740051..4831730a7 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java @@ -30,20 +30,20 @@ import java.util.Optional; * The class representing a service discovery pattern. */ public class ServiceDiscoveryService { - private Map> services; + private Map> services; - public Optional find(String service) { - return Optional.ofNullable(services.getOrDefault(service, null)); - } + 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 discover(OrchestrationChapter orchestrationChapterService) { + services.put(orchestrationChapterService.getName(), orchestrationChapterService); + return this; + } - public ServiceDiscoveryService() { - this.services = new HashMap<>(); - } + public ServiceDiscoveryService() { + this.services = new HashMap<>(); + } } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java index dad15cec3..f5a0c90f3 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -26,19 +26,19 @@ package com.iluwatar.saga.orchestration; * Class representing a service to withdraw a money */ public class WithdrawMoneyService extends Service { - @Override - public String getName() { - return "withdrawing Money"; - } + @Override + public String getName() { + return "withdrawing Money"; + } - @Override - public ChapterResult process(String value) { - if (value.equals("bad_order") || value.equals("crashed_order")) { - logger.info("The chapter '{}' has been started. But the exception has been raised." + - "The rollback is about to start", - getName(), value); - return ChapterResult.failure(value); - } - return super.process(value); + @Override + public ChapterResult process(String value) { + if (value.equals("bad_order") || value.equals("crashed_order")) { + LOGGER.info("The chapter '{}' has been started. But the exception has been raised." + + "The rollback is about to start", + getName(), value); + return ChapterResult.failure(value); } + return super.process(value); + } } diff --git a/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java index 5802f54ec..d7a2a34b2 100644 --- a/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java +++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java @@ -25,11 +25,13 @@ package com.iluwatar.saga.choreography; import com.iluwatar.saga.orchestration.SagaApplication; import org.junit.Test; -import static org.junit.Assert.*; +/*** + * empty test + */ public class SagaApplicationTest { - @Test - public void mainTest() { - SagaApplication.main(new String[]{}); - } + @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 index 8c8daa7c9..4300d4057 100644 --- a/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java +++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java @@ -25,35 +25,38 @@ 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(); - Chapter service = sd.findAny(); - Saga badOrderSaga = service.execute(newSaga("bad_order")); - Saga goodOrderSaga = service.execute(newSaga("good_order")); + @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); - } + 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 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)); - } + 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 4e4d86644..aa3c8773f 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java @@ -26,10 +26,13 @@ import org.junit.Test; import static org.junit.Assert.*; +/** + * empty test + */ public class SagaApplicationTest { - @Test - public void mainTest() { - SagaApplication.main(new String[]{}); - } + @Test + public void mainTest() { + SagaApplication.main(new String[]{}); + } } \ No newline at end of file diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java index ce3b926a3..a93bf5280 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java @@ -28,112 +28,118 @@ import org.junit.Test; import java.util.ArrayList; import java.util.List; +/** + * test to test orchestration logic + */ public class SagaOrchestratorInternallyTest { - private List records = new ArrayList<>(); + private List records = new ArrayList<>(); - @Test - public void executeTest() { - SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); - Saga.Result result = sagaOrchestrator.execute(1); - Assert.assertEquals(result, Saga.Result.ROLLBACK); - Assert.assertArrayEquals( - records.toArray(new String[]{}), - new String[]{"+1","+2","+3","+4","-4","-3","-2","-1"}); + @Test + public void executeTest() { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + Saga.Result result = sagaOrchestrator.execute(1); + Assert.assertEquals(result, Saga.Result.ROLLBACK); + Assert.assertArrayEquals( + records.toArray(new String[]{}), + new String[]{"+1", "+2", "+3", "+4", "-4", "-3", "-2", "-1"}); + } + + private static Saga newSaga() { + return Saga + .create() + .chapter("1") + .chapter("2") + .chapter("3") + .chapter("4"); + } + + private ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new Service1()) + .discover(new Service2()) + .discover(new Service3()) + .discover(new Service4()); + } + + class Service1 extends Service { + + @Override + public String getName() { + return "1"; } - private static Saga newSaga() { - return Saga - .create() - .chapter("1") - .chapter("2") - .chapter("3") - .chapter("4"); + @Override + public ChapterResult process(Integer value) { + records.add("+1"); + return ChapterResult.success(value); } - private ServiceDiscoveryService serviceDiscovery() { - return - new ServiceDiscoveryService() - .discover(new Service1()) - .discover(new Service2()) - .discover(new Service3()) - .discover(new Service4()); + @Override + public ChapterResult rollback(Integer value) { + records.add("-1"); + return ChapterResult.success(value); + } + } + + class Service2 extends Service { + + @Override + public String getName() { + return "2"; } - class Service1 extends Service { - - @Override - public String getName() { - return "1"; - } - - @Override - public ChapterResult process(Integer value) { - records.add("+1"); - return ChapterResult.success(value); - } - - @Override - public ChapterResult rollback(Integer value) { - records.add("-1"); - return ChapterResult.success(value); - } + @Override + public ChapterResult process(Integer value) { + records.add("+2"); + return ChapterResult.success(value); } - class Service2 extends Service { + @Override + public ChapterResult rollback(Integer value) { + records.add("-2"); + return ChapterResult.success(value); + } + } - @Override - public String getName() { - return "2"; - } - @Override - public ChapterResult process(Integer value) { - records.add("+2"); - return ChapterResult.success(value); - } + class Service3 extends Service { - @Override - public ChapterResult rollback(Integer value) { - records.add("-2"); - return ChapterResult.success(value); - } + @Override + public String getName() { + return "3"; } - class Service3 extends Service { - - @Override - public String getName() { - return "3"; - } - @Override - public ChapterResult process(Integer value) { - records.add("+3"); - return ChapterResult.success(value); - } - - @Override - public ChapterResult rollback(Integer value) { - records.add("-3"); - return ChapterResult.success(value); - } + @Override + public ChapterResult process(Integer value) { + records.add("+3"); + return ChapterResult.success(value); } - class Service4 extends Service { - - @Override - public String getName() { - return "4"; - } - @Override - public ChapterResult process(Integer value) { - records.add("+4"); - return ChapterResult.failure(value); - } - - @Override - public ChapterResult rollback(Integer value) { - records.add("-4"); - return ChapterResult.success(value); - } + @Override + public ChapterResult rollback(Integer value) { + records.add("-3"); + return ChapterResult.success(value); } + } + + class Service4 extends Service { + + @Override + public String getName() { + return "4"; + } + + @Override + public ChapterResult process(Integer value) { + records.add("+4"); + return ChapterResult.failure(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-4"); + return ChapterResult.success(value); + } + } } \ No newline at end of file diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java index bead0343d..d3522418c 100644 --- a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java @@ -25,33 +25,36 @@ 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"); + @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); - } + 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 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()); - } + private static ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new OrderService()) + .discover(new FlyBookingService()) + .discover(new HotelBookingService()) + .discover(new WithdrawMoneyService()); + } } \ No newline at end of file From 87af122509a4b91045a9670d317b9e5eb7d08f3b Mon Sep 17 00:00:00 2001 From: Besok Date: Wed, 13 Nov 2019 21:01:10 +0000 Subject: [PATCH 19/19] add changes according to google style --- .../choreography/ChoreographyChapter.java | 4 ++- .../saga/choreography/FlyBookingService.java | 3 ++- .../choreography/HotelBookingService.java | 3 ++- .../saga/choreography/OrderService.java | 1 + .../com/iluwatar/saga/choreography/Saga.java | 26 +++++++++--------- .../saga/choreography/SagaApplication.java | 26 ++++++++++-------- .../iluwatar/saga/choreography/Service.java | 14 ++++++---- .../choreography/ServiceDiscoveryService.java | 4 +-- .../choreography/WithdrawMoneyService.java | 4 +-- .../saga/orchestration/ChapterResult.java | 5 ++-- .../saga/orchestration/FlyBookingService.java | 3 ++- .../orchestration/HotelBookingService.java | 6 +++-- .../orchestration/OrchestrationChapter.java | 2 ++ .../saga/orchestration/OrderService.java | 1 + .../com/iluwatar/saga/orchestration/Saga.java | 5 ++-- .../saga/orchestration/SagaApplication.java | 27 +++++++++++-------- .../saga/orchestration/SagaOrchestrator.java | 13 +++++---- .../iluwatar/saga/orchestration/Service.java | 9 ++++--- .../ServiceDiscoveryService.java | 1 + .../orchestration/WithdrawMoneyService.java | 3 ++- 20 files changed, 98 insertions(+), 62 deletions(-) diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java index caa377a1e..c79a77a9a 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java @@ -20,6 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + package com.iluwatar.saga.choreography; @@ -31,7 +32,7 @@ package com.iluwatar.saga.choreography; public interface ChoreographyChapter { /** - * In that case, every method is responsible to make a decision on what to do then + * In that case, every method is responsible to make a decision on what to do then. * * @param saga incoming saga * @return saga result @@ -39,6 +40,7 @@ public interface ChoreographyChapter { Saga execute(Saga saga); /** + * get name method. * @return service name. */ String getName(); diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java index 60b183be3..4a9b1e804 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.java @@ -20,11 +20,12 @@ * 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 + * Class representing a service to book a fly. */ public class FlyBookingService extends Service { public FlyBookingService(ServiceDiscoveryService service) { diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java index 0bbf1d3e3..ee623482e 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java @@ -20,11 +20,12 @@ * 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 + * Class representing a service to book a hotel. */ public class HotelBookingService extends Service { public HotelBookingService(ServiceDiscoveryService service) { diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java b/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java index 3a2c002b1..19586a5a3 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.java @@ -20,6 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + package com.iluwatar.saga.choreography; 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 35069b80f..b591adaf2 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java @@ -20,6 +20,7 @@ * 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; @@ -44,7 +45,7 @@ public class Saga { } /** - * get resuzlt of saga + * get resuzlt of saga. * * @return result of saga @see {@link SagaResult} */ @@ -59,7 +60,7 @@ public class Saga { } /** - * add chapter to saga + * add chapter to saga. * @param name chapter name * @return this */ @@ -69,7 +70,7 @@ public class Saga { } /** - * set value to last chapter + * set value to last chapter. * @param value invalue * @return this */ @@ -82,7 +83,7 @@ public class Saga { } /** - * get value from current chapter + * get value from current chapter. * @return value */ public Object getCurrentValue() { @@ -90,7 +91,7 @@ public class Saga { } /** - * set value to current chapter + * set value to current chapter. * @param value to set */ public void setCurrentValue(Object value) { @@ -98,7 +99,7 @@ public class Saga { } /** - * set status for current chapter + * set status for current chapter. * @param result to set */ public void setCurrentStatus(ChapterResult result) { @@ -143,8 +144,9 @@ public class Saga { return chapters.get(pos).isSuccess(); } - /*** - * Class presents a chapter status and incoming parameters(incoming parameter transforms to outcoming parameter) + /** + * Class presents a chapter status and incoming + * parameters(incoming parameter transforms to outcoming parameter). */ public static class Chapter { private String name; @@ -170,7 +172,7 @@ public class Saga { } /** - * set result + * set result. * @param result {@link ChapterResult} */ public void setResult(ChapterResult result) { @@ -178,7 +180,7 @@ public class Saga { } /** - * the result for chapter is good + * the result for chapter is good. * @return true if is good otherwise bad */ public boolean isSuccess() { @@ -188,14 +190,14 @@ public class Saga { /** - * result for chapter + * result for chapter. */ public enum ChapterResult { INIT, SUCCESS, ROLLBACK } /** - * result for saga + * result for saga. */ public enum SagaResult { PROGRESS, FINISHED, ROLLBACKED diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java index 3aeb8672c..d8844c864 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java @@ -20,25 +20,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.saga.choreography; +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 + * 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. + * + *

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) + * + *

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 @@ -47,7 +51,7 @@ public class SagaApplication { private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class); /** - * main method + * main method. */ public static void main(String[] args) { ServiceDiscoveryService sd = serviceDiscovery(); diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Service.java b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java index a602b1147..2e932b528 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/Service.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java @@ -20,15 +20,16 @@ * 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; -import java.util.function.Supplier; /** - * Common abstraction class representing services + * Common abstraction class representing services. * implementing a general contract @see {@link ChoreographyChapter} */ public abstract class Service implements ChoreographyChapter { @@ -73,13 +74,15 @@ public abstract class Service implements ChoreographyChapter { } private Supplier serviceNotFoundException(String chServiceName) { - return () -> new RuntimeException(String.format("the service %s has not been found", 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", + 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); @@ -89,7 +92,8 @@ public abstract class Service implements ChoreographyChapter { @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", + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been rollbacked successfully", getName(), inValue); saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK); diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java index 919c99f02..a616ff4a5 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java @@ -20,8 +20,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.saga.choreography; +package com.iluwatar.saga.choreography; import java.util.HashMap; import java.util.Map; @@ -35,7 +35,7 @@ public class ServiceDiscoveryService { private Map services; /** - * find any service + * find any service. * * @return any service * @throws NoSuchElementException if no elements further diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java index b30a05e95..637e8ac1b 100644 --- a/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java @@ -20,11 +20,11 @@ * 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 + * Class representing a service to withdraw a money. */ public class WithdrawMoneyService extends Service { diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java index a8a1e7b5e..ef34ddb98 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java @@ -20,10 +20,11 @@ * 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 + * Executing result for chapter. * * @param incoming value */ @@ -53,7 +54,7 @@ public class ChapterResult { } /** - * state for chapter + * state for chapter. */ public enum State { SUCCESS, FAILURE diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java index 5dc0577bc..23b612957 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -20,10 +20,11 @@ * 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 + * Class representing a service to book a fly. */ public class FlyBookingService extends Service { @Override diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java index 51d8d5aad..2d6ba1389 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -20,10 +20,11 @@ * 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 + * Class representing a service to book a hotel. */ public class HotelBookingService extends Service { @Override @@ -42,7 +43,8 @@ public class HotelBookingService extends Service { return ChapterResult.failure(value); } - LOGGER.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been rollbacked successfully", getName(), value); return super.rollback(value); diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java index 861382a00..7e9e3581f 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java @@ -20,6 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + package com.iluwatar.saga.orchestration; /** @@ -30,6 +31,7 @@ package com.iluwatar.saga.orchestration; public interface OrchestrationChapter { /** + * method get name. * @return service name. */ String getName(); diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java index 32b9ce90b..c42a9d7a1 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -20,6 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + package com.iluwatar.saga.orchestration; /** 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 e42b06f9a..0d53362aa 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -20,6 +20,7 @@ * 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; @@ -60,14 +61,14 @@ public class Saga { } /** - * result for saga + * result for saga. */ public enum Result { FINISHED, ROLLBACK, CRASHED } /** - * class represents chapter name + * class represents chapter name. */ public static class Chapter { String name; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java index 6d7e9fd8d..830f5e653 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -23,23 +23,27 @@ 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 + * This pattern is used in distributed services to perform + * a group of operations atomically. + * This is an analog of transaction in a database but in terms + * of microservices architecture this is executed * in a distributed environment - *

- * A saga is a sequence of local transactions in a certain context. If one transaction fails for some reason, - * the saga executes compensating transactions(rollbacks) to undo the impact of the preceding transactions. - *

- * In this approach, there is an orchestrator @see {@link SagaOrchestrator} + * + *

A saga is a sequence of local transactions in a certain context. + * If one transaction fails for some reason, + * the saga executes compensating transactions(rollbacks) + * to undo the impact of the preceding transactions. + * + *

In this approach, there is an orchestrator @see {@link SagaOrchestrator} * that manages all the transactions and directs * the participant services to execute local transactions based on events. * The major difference with choreography saga is an ability to handle crashed services - * (otherwise in choreography services very hard to prevent a saga if one of them has been crashed) + * (otherwise in choreography services very hard to prevent a saga + * if one of them has been crashed) * * @see Saga * @see SagaOrchestrator @@ -49,7 +53,7 @@ public class SagaApplication { private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class); /** - * method to show common saga logic + * method to show common saga logic. */ public static void main(String[] args) { SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); @@ -58,7 +62,8 @@ public class SagaApplication { 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); + LOGGER.info("orders: goodOrder is {}, badOrder is {},crashedOrder is {}", + goodOrder, badOrder, crashedOrder); } 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 2f25ebd46..beec37655 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -20,14 +20,17 @@ * 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; -import java.util.Optional; - -import static com.iluwatar.saga.orchestration.Saga.Result.*; /** * The orchestrator that manages all the transactions and directs @@ -41,7 +44,7 @@ public class SagaOrchestrator { /** - * Create a new service to orchetrate sagas + * Create a new service to orchetrate sagas. * @param saga saga to process * @param sd service discovery @see {@link ServiceDiscoveryService} */ @@ -52,7 +55,7 @@ public class SagaOrchestrator { } /** - * pipeline to execute saga process/story + * 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/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java index c4ba2a9e1..d2b065201 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java @@ -20,13 +20,14 @@ * 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 + * Common abstraction class representing services. * implementing a general contract @see {@link OrchestrationChapter} * * @param type of incoming param @@ -40,14 +41,16 @@ public abstract class Service implements OrchestrationChapter { @Override public ChapterResult process(K value) { - LOGGER.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully", + LOGGER.info("The chapter '{}' has been started. " + + "The data {} has been stored or calculated successfully", getName(), value); return ChapterResult.success(value); } @Override public ChapterResult rollback(K value) { - LOGGER.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully", + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been rollbacked successfully", getName(), value); return ChapterResult.success(value); } diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java index 4831730a7..dbc6e7eb5 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java @@ -20,6 +20,7 @@ * 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; diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java index f5a0c90f3..7eb5634ef 100644 --- a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -20,10 +20,11 @@ * 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 + * Class representing a service to withdraw a money. */ public class WithdrawMoneyService extends Service { @Override