From cc571f4149274b77c3bd5c7d0aed60b23833462c Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 14 Nov 2019 06:03:50 +0000 Subject: [PATCH 1/4] Saga pattern (#1062) * init repo for role object * add to init * add to init * add first impl * add pattern * add license * add changes * add saga init dsc * add init saga dsc * add changes to dsc * add * add orchestrator * add ch * separate pkgs * add info * add choreogr * rem space * change according to cgeckstyle * add changes according to google style --- pom.xml | 2 + .../com/iluwatar/roleobject/InvestorRole.java | 2 +- saga/README.md | 46 ++++ saga/pom.xml | 45 ++++ .../choreography/ChoreographyChapter.java | 65 ++++++ .../saga/choreography/FlyBookingService.java | 39 ++++ .../choreography/HotelBookingService.java | 41 ++++ .../saga/choreography/OrderService.java | 40 ++++ .../com/iluwatar/saga/choreography/Saga.java | 217 ++++++++++++++++++ .../saga/choreography/SagaApplication.java | 84 +++++++ .../iluwatar/saga/choreography/Service.java | 113 +++++++++ .../choreography/ServiceDiscoveryService.java | 61 +++++ .../choreography/WithdrawMoneyService.java | 53 +++++ .../saga/orchestration/ChapterResult.java | 62 +++++ .../saga/orchestration/FlyBookingService.java | 34 +++ .../orchestration/HotelBookingService.java | 52 +++++ .../orchestration/OrchestrationChapter.java | 55 +++++ .../saga/orchestration/OrderService.java | 34 +++ .../com/iluwatar/saga/orchestration/Saga.java | 84 +++++++ .../saga/orchestration/SagaApplication.java | 87 +++++++ .../saga/orchestration/SagaOrchestrator.java | 148 ++++++++++++ .../iluwatar/saga/orchestration/Service.java | 59 +++++ .../ServiceDiscoveryService.java | 50 ++++ .../orchestration/WithdrawMoneyService.java | 45 ++++ .../choreography/SagaApplicationTest.java | 37 +++ .../choreography/SagaChoreographyTest.java | 62 +++++ .../orchestration/SagaApplicationTest.java | 38 +++ .../SagaOrchestratorInternallyTest.java | 145 ++++++++++++ .../orchestration/SagaOrchestratorTest.java | 60 +++++ 29 files changed, 1859 insertions(+), 1 deletion(-) create mode 100644 saga/README.md create mode 100644 saga/pom.xml create mode 100644 saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java 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/Saga.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 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/OrchestrationChapter.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/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/WithdrawMoneyService.java 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 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/pom.xml b/pom.xml index 6a666caab..1ab896068 100644 --- a/pom.xml +++ b/pom.xml @@ -183,6 +183,8 @@ data-locality subclass-sandbox circuit-breaker + role-object + saga double-buffer sharding 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); } } diff --git a/saga/README.md b/saga/README.md new file mode 100644 index 000000000..546ad598b --- /dev/null +++ b/saga/README.md @@ -0,0 +1,46 @@ +--- +layout: pattern +title: Saga +folder: saga +permalink: /patterns/saga/ +categories: Behavioral +tags: + - Java + - Difficulty-Expert + - Idiom + - Distributed communication +--- + +## Also known as +This pattern has a similar goal with two-phase commit (XA transaction) + +## Intent +This pattern is used in distributed services to perform a group of operations atomically. +This is an analog of transaction in a database but in terms of microservices architecture this is executed +in a distributed environment + +## Explanation +A saga is a sequence of local transactions in a certain context. If one transaction fails for some reason, +the saga executes compensating transactions(rollbacks) to undo the impact of the preceding transactions. +There are two types of Saga: + +- Choreography-Based Saga. +In this approach, there is no central orchestrator. +Each service participating in the Saga performs their transaction and publish events. +The other services act upon those events and perform their transactions. +Also, they may or not publish other events based on the situation. + +- Orchestration-Based Saga +In this approach, there is a Saga orchestrator that manages all the transactions and directs +the participant services to execute local transactions based on events. +This orchestrator can also be though of as a Saga Manager. + +## Applicability +Use the Saga pattern, if: +- you need to perform a group of operations related to different microservices atomically +- you need to rollback changes in different places in case of failure one of the operation +- you need to take care of data consistency in different places including different databases +- you can not use 2PC(two phase commit) + +## Credits +- [Pattern: Saga](https://microservices.io/patterns/data/saga.html) \ No newline at end of file diff --git a/saga/pom.xml b/saga/pom.xml new file mode 100644 index 000000000..111f4e7b8 --- /dev/null +++ b/saga/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.22.0-SNAPSHOT + + + saga + + + junit + junit + test + + + + diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java new file mode 100644 index 000000000..c79a77a9a --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ChoreographyChapter.java @@ -0,0 +1,65 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + + +/** + * ChoreographyChapter is an interface representing a contract for an external service. + * In that case, a service needs to make a decision what to do further + * hence the server needs to get all context representing {@link Saga} + */ +public interface ChoreographyChapter { + + /** + * In that case, every method is responsible to make a decision on what to do then. + * + * @param saga incoming saga + * @return saga result + */ + Saga execute(Saga saga); + + /** + * get name method. + * @return service name. + */ + String getName(); + + /** + * The operation executed in general case. + * + * @param saga incoming saga + * @return result {@link Saga} + */ + Saga process(Saga saga); + + /** + * The operation executed in rollback case. + * + * @param saga incoming saga + * @return result {@link Saga} + */ + Saga rollback(Saga saga); + + +} 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..4a9b1e804 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/FlyBookingService.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 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..ee623482e --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/HotelBookingService.java @@ -0,0 +1,41 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + + +/** + * Class representing a service to book a hotel. + */ +public class HotelBookingService extends Service { + public HotelBookingService(ServiceDiscoveryService service) { + super(service); + } + + @Override + public String getName() { + return "booking a Hotel"; + } + + +} 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..19586a5a3 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/OrderService.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 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 new file mode 100644 index 000000000..b591adaf2 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Saga.java @@ -0,0 +1,217 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Saga representation. + * Saga consists of chapters. + * Every ChoreographyChapter is executed a certain service. + */ +public class Saga { + + private List chapters; + private int pos; + private boolean forward; + private boolean finished; + + + public static Saga create() { + return new Saga(); + } + + /** + * get resuzlt of saga. + * + * @return result of saga @see {@link SagaResult} + */ + public SagaResult getResult() { + if (finished) { + return forward + ? SagaResult.FINISHED + : SagaResult.ROLLBACKED; + } + + return SagaResult.PROGRESS; + } + + /** + * add chapter to saga. + * @param name chapter name + * @return this + */ + public Saga chapter(String name) { + this.chapters.add(new Chapter(name)); + return this; + } + + /** + * set value to last chapter. + * @param value invalue + * @return this + */ + public Saga setInValue(Object value) { + if (chapters.isEmpty()) { + return this; + } + chapters.get(chapters.size() - 1).setInValue(value); + return this; + } + + /** + * get value from current chapter. + * @return value + */ + public Object getCurrentValue() { + return chapters.get(pos).getInValue(); + } + + /** + * set value to current chapter. + * @param value to set + */ + public void setCurrentValue(Object value) { + chapters.get(pos).setInValue(value); + } + + /** + * set status for current chapter. + * @param result to set + */ + public void setCurrentStatus(ChapterResult result) { + chapters.get(pos).setResult(result); + } + + void setFinished(boolean finished) { + this.finished = finished; + } + + boolean isForward() { + return forward; + } + + int forward() { + return ++pos; + } + + int back() { + this.forward = false; + return --pos; + } + + + private Saga() { + this.chapters = new ArrayList<>(); + this.pos = 0; + this.forward = true; + this.finished = false; + } + + Chapter getCurrent() { + return chapters.get(pos); + } + + + boolean isPresent() { + return pos >= 0 && pos < chapters.size(); + } + + boolean isCurrentSuccess() { + return chapters.get(pos).isSuccess(); + } + + /** + * Class presents a chapter status and incoming + * parameters(incoming parameter transforms to outcoming parameter). + */ + public static class Chapter { + private String name; + private ChapterResult result; + private Object inValue; + + + public Chapter(String name) { + this.name = name; + this.result = ChapterResult.INIT; + } + + public Object getInValue() { + return inValue; + } + + public void setInValue(Object object) { + this.inValue = object; + } + + public String getName() { + return name; + } + + /** + * set result. + * @param result {@link ChapterResult} + */ + public void setResult(ChapterResult result) { + this.result = result; + } + + /** + * the result for chapter is good. + * @return true if is good otherwise bad + */ + public boolean isSuccess() { + return result == ChapterResult.SUCCESS; + } + } + + + /** + * result for chapter. + */ + public enum ChapterResult { + INIT, SUCCESS, ROLLBACK + } + + /** + * result for saga. + */ + public enum SagaResult { + PROGRESS, FINISHED, ROLLBACKED + } + + @Override + public String toString() { + return "Saga{" + + "chapters=" + + Arrays.toString(chapters.toArray()) + + ", pos=" + + pos + + ", forward=" + + forward + + '}'; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java new file mode 100644 index 000000000..d8844c864 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/SagaApplication.java @@ -0,0 +1,84 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This pattern is used in distributed services to perform a group of operations atomically. + * This is an analog of transaction in a database but in terms + * of microservices architecture this is executed + * in a distributed environment + * + *

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

In this approach, there are no mediators or orchestrators services. + * All chapters are handled and moved by services manually. + * + *

The major difference with choreography saga is an ability to handle crashed services + * (otherwise in choreography services very hard to prevent a saga + * if one of them has been crashed) + * + * @see com.iluwatar.saga.choreography.Saga + * @see Service + */ +public class SagaApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class); + + /** + * main method. + */ + public static void main(String[] args) { + ServiceDiscoveryService sd = serviceDiscovery(); + ChoreographyChapter service = sd.findAny(); + Saga goodOrderSaga = service.execute(newSaga("good_order")); + Saga badOrderSaga = service.execute(newSaga("bad_order")); + LOGGER.info("orders: goodOrder is {}, badOrder is {}", + goodOrderSaga.getResult(), badOrderSaga.getResult()); + + } + + + private static Saga newSaga(Object value) { + return Saga + .create() + .chapter("init an order").setInValue(value) + .chapter("booking a Fly") + .chapter("booking a Hotel") + .chapter("withdrawing Money"); + } + + private static ServiceDiscoveryService serviceDiscovery() { + ServiceDiscoveryService sd = new ServiceDiscoveryService(); + return sd + .discover(new OrderService(sd)) + .discover(new FlyBookingService(sd)) + .discover(new HotelBookingService(sd)) + .discover(new WithdrawMoneyService(sd)); + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/Service.java b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java new file mode 100644 index 000000000..2e932b528 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/Service.java @@ -0,0 +1,113 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + +import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Common abstraction class representing services. + * implementing a general contract @see {@link ChoreographyChapter} + */ +public abstract class Service implements ChoreographyChapter { + protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class); + + private final ServiceDiscoveryService sd; + + public Service(ServiceDiscoveryService service) { + this.sd = service; + } + + @Override + public Saga execute(Saga saga) { + Saga nextSaga = saga; + Object nextVal; + String chapterName = saga.getCurrent().getName(); + if (chapterName.equals(getName())) { + if (saga.isForward()) { + nextSaga = process(saga); + nextVal = nextSaga.getCurrentValue(); + if (nextSaga.isCurrentSuccess()) { + nextSaga.forward(); + } else { + nextSaga.back(); + } + } else { + nextSaga = rollback(saga); + nextVal = nextSaga.getCurrentValue(); + nextSaga.back(); + } + + if (isSagaFinished(nextSaga)) { + return nextSaga; + } + + nextSaga.setCurrentValue(nextVal); + } + Saga finalNextSaga = nextSaga; + + return sd.find(chapterName).map(ch -> ch.execute(finalNextSaga)) + .orElseThrow(serviceNotFoundException(chapterName)); + } + + private Supplier serviceNotFoundException(String chServiceName) { + return () -> new RuntimeException( + String.format("the service %s has not been found", chServiceName)); + } + + @Override + public Saga process(Saga saga) { + Object inValue = saga.getCurrentValue(); + LOGGER.info("The chapter '{}' has been started. " + + "The data {} has been stored or calculated successfully", + getName(), inValue); + saga.setCurrentStatus(Saga.ChapterResult.SUCCESS); + saga.setCurrentValue(inValue); + return saga; + } + + @Override + public Saga rollback(Saga saga) { + Object inValue = saga.getCurrentValue(); + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been rollbacked successfully", + getName(), inValue); + + saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK); + saga.setCurrentValue(inValue); + return saga; + } + + private boolean isSagaFinished(Saga saga) { + if (!saga.isPresent()) { + saga.setFinished(true); + LOGGER.info(" the saga has been finished with {} status", saga.getResult()); + return true; + } + return false; + } + +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java new file mode 100644 index 000000000..a616ff4a5 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/ServiceDiscoveryService.java @@ -0,0 +1,61 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; + +/** + * The class representing a service discovery pattern. + */ +public class ServiceDiscoveryService { + private Map services; + + /** + * find any service. + * + * @return any service + * @throws NoSuchElementException if no elements further + */ + public ChoreographyChapter findAny() { + return services.values().iterator().next(); + } + + public Optional find(String service) { + return Optional.ofNullable(services.getOrDefault(service, null)); + } + + public ServiceDiscoveryService discover(ChoreographyChapter chapterService) { + services.put(chapterService.getName(), chapterService); + return this; + } + + public ServiceDiscoveryService() { + this.services = new HashMap<>(); + } + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java new file mode 100644 index 000000000..637e8ac1b --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/choreography/WithdrawMoneyService.java @@ -0,0 +1,53 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.choreography; + +/** + * Class representing a service to withdraw a money. + */ +public class WithdrawMoneyService extends Service { + + public WithdrawMoneyService(ServiceDiscoveryService service) { + super(service); + } + + @Override + public String getName() { + return "withdrawing Money"; + } + + @Override + public Saga process(Saga saga) { + Object inValue = saga.getCurrentValue(); + + if (inValue.equals("bad_order")) { + LOGGER.info("The chapter '{}' has been started. But the exception has been raised." + + "The rollback is about to start", + getName(), inValue); + saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK); + return saga; + } + return super.process(saga); + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java new file mode 100644 index 000000000..ef34ddb98 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ChapterResult.java @@ -0,0 +1,62 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +/** + * Executing result for chapter. + * + * @param incoming value + */ +public class ChapterResult { + private K value; + private State state; + + public K getValue() { + return value; + } + + ChapterResult(K value, State state) { + this.value = value; + this.state = state; + } + + public boolean isSuccess() { + return state == State.SUCCESS; + } + + public static ChapterResult success(K val) { + return new ChapterResult<>(val, State.SUCCESS); + } + + public static ChapterResult failure(K val) { + return new ChapterResult<>(val, State.FAILURE); + } + + /** + * state for chapter. + */ + public enum State { + SUCCESS, FAILURE + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java new file mode 100644 index 000000000..23b612957 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/FlyBookingService.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +/** + * Class representing a service to book a fly. + */ +public class FlyBookingService extends Service { + @Override + public String getName() { + return "booking a Fly"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java new file mode 100644 index 000000000..2d6ba1389 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/HotelBookingService.java @@ -0,0 +1,52 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +/** + * Class representing a service to book a hotel. + */ +public class HotelBookingService extends Service { + @Override + public String getName() { + return "booking a Hotel"; + } + + + @Override + public ChapterResult rollback(String value) { + if (value.equals("crashed_order")) { + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been failed.The saga has been crashed.", + getName(), value); + + return ChapterResult.failure(value); + } + + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been rollbacked successfully", + getName(), value); + + return super.rollback(value); + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java new file mode 100644 index 000000000..7e9e3581f --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrchestrationChapter.java @@ -0,0 +1,55 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +/** + * ChoreographyChapter is an interface representing a contract for an external service. + * + * @param is type for passing params + */ +public interface OrchestrationChapter { + + /** + * method get name. + * @return service name. + */ + String getName(); + + /** + * The operation executed in general case. + * + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult process(K value); + + /** + * The operation executed in rollback case. + * + * @param value incoming value + * @return result {@link ChapterResult} + */ + ChapterResult rollback(K value); + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java new file mode 100644 index 000000000..c42a9d7a1 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/OrderService.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +/** + * Class representing a service to init a new order. + */ +public class OrderService extends Service { + @Override + public String getName() { + return "init an order"; + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java new file mode 100644 index 000000000..0d53362aa --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java @@ -0,0 +1,84 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +import java.util.ArrayList; +import java.util.List; + +/** + * Saga representation. + * Saga consists of chapters. + * Every ChoreographyChapter is executed by a certain service. + */ +public class Saga { + + private List chapters; + + + private Saga() { + this.chapters = new ArrayList<>(); + } + + + public Saga chapter(String name) { + this.chapters.add(new Chapter(name)); + return this; + } + + + public Chapter get(int idx) { + return chapters.get(idx); + } + + public boolean isPresent(int idx) { + return idx >= 0 && idx < chapters.size(); + } + + + public static Saga create() { + return new Saga(); + } + + /** + * result for saga. + */ + public enum Result { + FINISHED, ROLLBACK, CRASHED + } + + /** + * class represents chapter name. + */ + public static class Chapter { + String name; + + public Chapter(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java new file mode 100644 index 000000000..830f5e653 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaApplication.java @@ -0,0 +1,87 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This pattern is used in distributed services to perform + * a group of operations atomically. + * This is an analog of transaction in a database but in terms + * of microservices architecture this is executed + * in a distributed environment + * + *

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

In this approach, there is an orchestrator @see {@link SagaOrchestrator} + * that manages all the transactions and directs + * the participant services to execute local transactions based on events. + * The major difference with choreography saga is an ability to handle crashed services + * (otherwise in choreography services very hard to prevent a saga + * if one of them has been crashed) + * + * @see Saga + * @see SagaOrchestrator + * @see Service + */ +public class SagaApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class); + + /** + * method to show common saga logic. + */ + public static void main(String[] args) { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + + Saga.Result goodOrder = sagaOrchestrator.execute("good_order"); + Saga.Result badOrder = sagaOrchestrator.execute("bad_order"); + Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order"); + + LOGGER.info("orders: goodOrder is {}, badOrder is {},crashedOrder is {}", + goodOrder, badOrder, crashedOrder); + } + + + private static Saga newSaga() { + return Saga + .create() + .chapter("init an order") + .chapter("booking a Fly") + .chapter("booking a Hotel") + .chapter("withdrawing Money"); + } + + private static ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new OrderService()) + .discover(new FlyBookingService()) + .discover(new HotelBookingService()) + .discover(new WithdrawMoneyService()); + } +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java new file mode 100644 index 000000000..beec37655 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/SagaOrchestrator.java @@ -0,0 +1,148 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +import static com.iluwatar.saga.orchestration.Saga.Result.CRASHED; +import static com.iluwatar.saga.orchestration.Saga.Result.FINISHED; +import static com.iluwatar.saga.orchestration.Saga.Result.ROLLBACK; + +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The orchestrator that manages all the transactions and directs + * the participant services to execute local transactions based on events. + */ +public class SagaOrchestrator { + private static final Logger LOGGER = LoggerFactory.getLogger(SagaOrchestrator.class); + private final Saga saga; + private final ServiceDiscoveryService sd; + private final CurrentState state; + + + /** + * Create a new service to orchetrate sagas. + * @param saga saga to process + * @param sd service discovery @see {@link ServiceDiscoveryService} + */ + public SagaOrchestrator(Saga saga, ServiceDiscoveryService sd) { + this.saga = saga; + this.sd = sd; + this.state = new CurrentState(); + } + + /** + * pipeline to execute saga process/story. + * + * @param value incoming value + * @param type for incoming value + * @return result @see {@link Saga.Result} + */ + @SuppressWarnings("unchecked") + public Saga.Result execute(K value) { + state.cleanUp(); + LOGGER.info(" The new saga is about to start"); + Saga.Result result = FINISHED; + K tempVal = value; + + while (true) { + int next = state.current(); + Saga.Chapter ch = saga.get(next); + Optional srvOpt = sd.find(ch.name); + + if (!srvOpt.isPresent()) { + state.directionToBack(); + state.back(); + continue; + } + + OrchestrationChapter srv = srvOpt.get(); + + if (state.isForward()) { + ChapterResult processRes = srv.process(tempVal); + if (processRes.isSuccess()) { + next = state.forward(); + tempVal = (K) processRes.getValue(); + } else { + state.directionToBack(); + } + } else { + ChapterResult rlRes = srv.rollback(tempVal); + if (rlRes.isSuccess()) { + next = state.back(); + tempVal = (K) rlRes.getValue(); + } else { + result = CRASHED; + next = state.back(); + } + } + + + if (!saga.isPresent(next)) { + return state.isForward() ? FINISHED : result == CRASHED ? CRASHED : ROLLBACK; + } + } + + } + + + private static class CurrentState { + int currentNumber; + boolean isForward; + + void cleanUp() { + currentNumber = 0; + isForward = true; + } + + CurrentState() { + this.currentNumber = 0; + this.isForward = true; + } + + + boolean isForward() { + return isForward; + } + + void directionToBack() { + isForward = false; + } + + int forward() { + return ++currentNumber; + } + + int back() { + return --currentNumber; + } + + int current() { + return currentNumber; + } + } + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java new file mode 100644 index 000000000..d2b065201 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/Service.java @@ -0,0 +1,59 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Common abstraction class representing services. + * implementing a general contract @see {@link OrchestrationChapter} + * + * @param type of incoming param + */ +public abstract class Service implements OrchestrationChapter { + protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class); + + @Override + public abstract String getName(); + + + @Override + public ChapterResult process(K value) { + LOGGER.info("The chapter '{}' has been started. " + + "The data {} has been stored or calculated successfully", + getName(), value); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(K value) { + LOGGER.info("The Rollback for a chapter '{}' has been started. " + + "The data {} has been rollbacked successfully", + getName(), value); + return ChapterResult.success(value); + } + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java new file mode 100644 index 000000000..dbc6e7eb5 --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/ServiceDiscoveryService.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * The class representing a service discovery pattern. + */ +public class ServiceDiscoveryService { + private Map> services; + + public Optional find(String service) { + return Optional.ofNullable(services.getOrDefault(service, null)); + } + + public ServiceDiscoveryService discover(OrchestrationChapter orchestrationChapterService) { + services.put(orchestrationChapterService.getName(), orchestrationChapterService); + return this; + } + + public ServiceDiscoveryService() { + this.services = new HashMap<>(); + } + + +} diff --git a/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java new file mode 100644 index 000000000..7eb5634ef --- /dev/null +++ b/saga/src/main/java/com/iluwatar/saga/orchestration/WithdrawMoneyService.java @@ -0,0 +1,45 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.saga.orchestration; + +/** + * Class representing a service to withdraw a money. + */ +public class WithdrawMoneyService extends Service { + @Override + public String getName() { + return "withdrawing Money"; + } + + @Override + public ChapterResult process(String value) { + if (value.equals("bad_order") || value.equals("crashed_order")) { + LOGGER.info("The chapter '{}' has been started. But the exception has been raised." + + "The rollback is about to start", + getName(), value); + return ChapterResult.failure(value); + } + return super.process(value); + } +} diff --git a/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java new file mode 100644 index 000000000..d7a2a34b2 --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaApplicationTest.java @@ -0,0 +1,37 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.saga.choreography; + +import com.iluwatar.saga.orchestration.SagaApplication; +import org.junit.Test; + + +/*** + * empty test + */ +public class SagaApplicationTest { + @Test + public void mainTest() { + SagaApplication.main(new String[]{}); + } +} \ No newline at end of file diff --git a/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java b/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java new file mode 100644 index 000000000..4300d4057 --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/choreography/SagaChoreographyTest.java @@ -0,0 +1,62 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.saga.choreography; + +import org.junit.Assert; +import org.junit.Test; + +/** + * test to check choreography saga + */ +public class SagaChoreographyTest { + + + @Test + public void executeTest() { + ServiceDiscoveryService sd = serviceDiscovery(); + ChoreographyChapter service = sd.findAny(); + Saga badOrderSaga = service.execute(newSaga("bad_order")); + Saga goodOrderSaga = service.execute(newSaga("good_order")); + + Assert.assertEquals(badOrderSaga.getResult(), Saga.SagaResult.ROLLBACKED); + Assert.assertEquals(goodOrderSaga.getResult(), Saga.SagaResult.FINISHED); + } + + private static Saga newSaga(Object value) { + return Saga + .create() + .chapter("init an order").setInValue(value) + .chapter("booking a Fly") + .chapter("booking a Hotel") + .chapter("withdrawing Money"); + } + + private static ServiceDiscoveryService serviceDiscovery() { + ServiceDiscoveryService sd = new ServiceDiscoveryService(); + return sd + .discover(new OrderService(sd)) + .discover(new FlyBookingService(sd)) + .discover(new HotelBookingService(sd)) + .discover(new WithdrawMoneyService(sd)); + } +} diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java new file mode 100644 index 000000000..aa3c8773f --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaApplicationTest.java @@ -0,0 +1,38 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.saga.orchestration; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * empty test + */ +public class SagaApplicationTest { + + @Test + public void mainTest() { + SagaApplication.main(new String[]{}); + } +} \ No newline at end of file diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java new file mode 100644 index 000000000..a93bf5280 --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorInternallyTest.java @@ -0,0 +1,145 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.saga.orchestration; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * test to test orchestration logic + */ +public class SagaOrchestratorInternallyTest { + + private List records = new ArrayList<>(); + + @Test + public void executeTest() { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + Saga.Result result = sagaOrchestrator.execute(1); + Assert.assertEquals(result, Saga.Result.ROLLBACK); + Assert.assertArrayEquals( + records.toArray(new String[]{}), + new String[]{"+1", "+2", "+3", "+4", "-4", "-3", "-2", "-1"}); + } + + private static Saga newSaga() { + return Saga + .create() + .chapter("1") + .chapter("2") + .chapter("3") + .chapter("4"); + } + + private ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new Service1()) + .discover(new Service2()) + .discover(new Service3()) + .discover(new Service4()); + } + + class Service1 extends Service { + + @Override + public String getName() { + return "1"; + } + + @Override + public ChapterResult process(Integer value) { + records.add("+1"); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-1"); + return ChapterResult.success(value); + } + } + + class Service2 extends Service { + + @Override + public String getName() { + return "2"; + } + + @Override + public ChapterResult process(Integer value) { + records.add("+2"); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-2"); + return ChapterResult.success(value); + } + } + + class Service3 extends Service { + + @Override + public String getName() { + return "3"; + } + + @Override + public ChapterResult process(Integer value) { + records.add("+3"); + return ChapterResult.success(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-3"); + return ChapterResult.success(value); + } + } + + class Service4 extends Service { + + @Override + public String getName() { + return "4"; + } + + @Override + public ChapterResult process(Integer value) { + records.add("+4"); + return ChapterResult.failure(value); + } + + @Override + public ChapterResult rollback(Integer value) { + records.add("-4"); + return ChapterResult.success(value); + } + } +} \ No newline at end of file diff --git a/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java new file mode 100644 index 000000000..d3522418c --- /dev/null +++ b/saga/src/test/java/com/iluwatar/saga/orchestration/SagaOrchestratorTest.java @@ -0,0 +1,60 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.saga.orchestration; + +import org.junit.Assert; +import org.junit.Test; + +/** + * test to check general logic + */ +public class SagaOrchestratorTest { + + @Test + public void execute() { + SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery()); + Saga.Result badOrder = sagaOrchestrator.execute("bad_order"); + Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order"); + + Assert.assertEquals(badOrder, Saga.Result.ROLLBACK); + Assert.assertEquals(crashedOrder, Saga.Result.CRASHED); + } + + private static Saga newSaga() { + return Saga + .create() + .chapter("init an order") + .chapter("booking a Fly") + .chapter("booking a Hotel") + .chapter("withdrawing Money"); + } + + private static ServiceDiscoveryService serviceDiscovery() { + return + new ServiceDiscoveryService() + .discover(new OrderService()) + .discover(new FlyBookingService()) + .discover(new HotelBookingService()) + .discover(new WithdrawMoneyService()); + } +} \ No newline at end of file From df8a4e3b47fa1b5a28c8581e687455eaba892496 Mon Sep 17 00:00:00 2001 From: Martin Vandenbussche Date: Sat, 16 Nov 2019 13:24:46 +0100 Subject: [PATCH 2/4] Adding parameterized specification (Issue#1055) (#1088) * Resolution proposition to Issue#1055 (UML diagram left to do) * Deciding not to modify the UML diagram for now --- specification/README.md | 33 +++++++++- specification/etc/specification.ucls | 18 ++--- .../com/iluwatar/specification/app/App.java | 21 +++++- .../creature/AbstractCreature.java | 13 +++- .../specification/creature/Creature.java | 3 + .../specification/creature/Dragon.java | 7 +- .../specification/creature/Goblin.java | 7 +- .../specification/creature/KillerBee.java | 7 +- .../specification/creature/Octopus.java | 7 +- .../specification/creature/Shark.java | 7 +- .../specification/creature/Troll.java | 7 +- .../specification/property/Color.java | 2 +- .../iluwatar/specification/property/Mass.java | 65 +++++++++++++++++++ .../selector/MassGreaterThanSelector.java | 44 +++++++++++++ .../selector/MassSmallerThanOrEqSelector.java | 44 +++++++++++++ .../specification/creature/CreatureTest.java | 23 ++++--- .../selector/MassSelectorTest.java | 50 ++++++++++++++ 17 files changed, 328 insertions(+), 30 deletions(-) create mode 100644 specification/src/main/java/com/iluwatar/specification/property/Mass.java create mode 100644 specification/src/main/java/com/iluwatar/specification/selector/MassGreaterThanSelector.java create mode 100644 specification/src/main/java/com/iluwatar/specification/selector/MassSmallerThanOrEqSelector.java create mode 100644 specification/src/test/java/com/iluwatar/specification/selector/MassSelectorTest.java diff --git a/specification/README.md b/specification/README.md index a72e253d7..bb95097e3 100644 --- a/specification/README.md +++ b/specification/README.md @@ -31,7 +31,7 @@ Use the Specification pattern when Real world example -> There is a pool of different creatures and we often need to select some subset of them. We can write our search specification such as "creatures that can fly" and give it to the party that will perform the filtering. +> There is a pool of different creatures and we often need to select some subset of them. We can write our search specification such as "creatures that can fly" or "creatures heavier than 500 kilograms" and give it to the party that will perform the filtering. In Plain Words @@ -43,7 +43,8 @@ Wikipedia says **Programmatic Example** -If we look at our creature pool example from above, we have a set of creatures with certain properties. +If we look at our creature pool example from above, we have a set of creatures with certain properties.\ +Those properties can be part of a pre-defined, limited set (represented here by the enums Size, Movement and Color); but they can also be discrete (e.g. the mass of a Creature). In this case, it is more appropriate to use what we call "parameterized specification", where the property value can be given as an argument when the Creature is created, allowing for more flexibility. ```java public interface Creature { @@ -51,6 +52,7 @@ public interface Creature { Size getSize(); Movement getMovement(); Color getColor(); + Mass getMass(); } ``` @@ -60,7 +62,7 @@ And dragon implementation looks like this. public class Dragon extends AbstractCreature { public Dragon() { - super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); + super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0)); } } ``` @@ -83,6 +85,24 @@ public class MovementSelector implements Predicate { } ``` +On the other hand, we selecting creatures heavier than a chosen amount, we use MassGreaterThanSelector. + +```java +public class MassGreaterThanSelector implements Predicate { + + private final Mass mass; + + public MassGreaterThanSelector(double mass) { + this.mass = new Mass(mass); + } + + @Override + public boolean test(Creature t) { + return t.getMass().greaterThan(mass); + } +} +``` + With these building blocks in place, we can perform a search for red and flying creatures like this. ```java @@ -90,6 +110,13 @@ With these building blocks in place, we can perform a search for red and flying .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))).collect(Collectors.toList()); ``` +But we could also use our paramterized selector like this. + +```java + List heavyCreatures = creatures.stream() + .filter(new MassGreaterThanSelector(500.0).collect(Collectors.toList()); +``` + ## Related patterns * Repository diff --git a/specification/etc/specification.ucls b/specification/etc/specification.ucls index 2edbbed0c..54b08cc41 100644 --- a/specification/etc/specification.ucls +++ b/specification/etc/specification.ucls @@ -117,7 +117,7 @@ - + @@ -126,7 +126,7 @@ - + @@ -186,13 +186,13 @@ - - + + - - - - + + + + @@ -202,7 +202,7 @@ - + diff --git a/specification/src/main/java/com/iluwatar/specification/app/App.java b/specification/src/main/java/com/iluwatar/specification/app/App.java index 8ca7283c9..c1ff8211e 100644 --- a/specification/src/main/java/com/iluwatar/specification/app/App.java +++ b/specification/src/main/java/com/iluwatar/specification/app/App.java @@ -31,8 +31,11 @@ import com.iluwatar.specification.creature.Octopus; import com.iluwatar.specification.creature.Shark; import com.iluwatar.specification.creature.Troll; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.selector.ColorSelector; +import com.iluwatar.specification.selector.MassGreaterThanSelector; +import com.iluwatar.specification.selector.MassSmallerThanOrEqSelector; import com.iluwatar.specification.selector.MovementSelector; import java.util.List; import java.util.stream.Collectors; @@ -51,7 +54,7 @@ import org.slf4j.LoggerFactory; *

http://martinfowler.com/apsupp/spec.pdf

*/ public class App { - + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); /** @@ -61,6 +64,8 @@ public class App { // initialize creatures list List creatures = List.of(new Goblin(), new Octopus(), new Dragon(), new Shark(), new Troll(), new KillerBee()); + // so-called "hard-coded" specification + LOGGER.info("Demonstrating hard-coded specification :"); // find all walking creatures LOGGER.info("Find all walking creatures"); List walkingCreatures = @@ -79,5 +84,19 @@ public class App { .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))) .collect(Collectors.toList()); redAndFlyingCreatures.forEach(c -> LOGGER.info(c.toString())); + // so-called "parameterized" specification + LOGGER.info("Demonstrating parameterized specification :"); + // find all creatures heavier than 500kg + LOGGER.info("Find all creatures heavier than 600kg"); + List heavyCreatures = + creatures.stream().filter(new MassGreaterThanSelector(600.0)) + .collect(Collectors.toList()); + heavyCreatures.forEach(c -> LOGGER.info(c.toString())); + // find all creatures heavier than 500kg + LOGGER.info("Find all creatures lighter than or weighing exactly 500kg"); + List lightCreatures = + creatures.stream().filter(new MassSmallerThanOrEqSelector(500.0)) + .collect(Collectors.toList()); + lightCreatures.forEach(c -> LOGGER.info(c.toString())); } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java b/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java index de7a41417..6b359d8ac 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -36,20 +37,23 @@ public abstract class AbstractCreature implements Creature { private Size size; private Movement movement; private Color color; + private Mass mass; /** * Constructor. */ - public AbstractCreature(String name, Size size, Movement movement, Color color) { + public AbstractCreature(String name, Size size, Movement movement, Color color, Mass mass) { this.name = name; this.size = size; this.movement = movement; this.color = color; + this.mass = mass; } @Override public String toString() { - return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color); + return String.format("%s [size=%s, movement=%s, color=%s, mass=%s]", + name, size, movement, color, mass); } @Override @@ -71,4 +75,9 @@ public abstract class AbstractCreature implements Creature { public Color getColor() { return color; } + + @Override + public Mass getMass() { + return mass; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Creature.java b/specification/src/main/java/com/iluwatar/specification/creature/Creature.java index 3f8ccdfdb..2eb41a6dc 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Creature.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Creature.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -39,4 +40,6 @@ public interface Creature { Movement getMovement(); Color getColor(); + + Mass getMass(); } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java b/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java index d4f5a7f23..937bd5497 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size; public class Dragon extends AbstractCreature { public Dragon() { - super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); + this(new Mass(39300.0)); + } + + public Dragon(Mass mass) { + super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, mass); } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java b/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java index 0b145b737..7b173d580 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size; public class Goblin extends AbstractCreature { public Goblin() { - super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN); + this(new Mass(30.0)); + } + + public Goblin(Mass mass) { + super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN, mass); } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java b/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java index 77f32c9f4..2b35714fc 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size; public class KillerBee extends AbstractCreature { public KillerBee() { - super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT); + this(new Mass(6.7)); + } + + public KillerBee(Mass mass) { + super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT, mass); } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java b/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java index 6958f7fbd..f516a6dc5 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size; public class Octopus extends AbstractCreature { public Octopus() { - super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK); + this(new Mass(12.0)); + } + + public Octopus(Mass mass) { + super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK, mass); } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Shark.java b/specification/src/main/java/com/iluwatar/specification/creature/Shark.java index b9e161da4..8788e85d7 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Shark.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Shark.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size; public class Shark extends AbstractCreature { public Shark() { - super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT); + this(new Mass(500.0)); + } + + public Shark(Mass mass) { + super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT, mass); } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Troll.java b/specification/src/main/java/com/iluwatar/specification/creature/Troll.java index 3f416bdd1..addcb94b6 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Troll.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Troll.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; @@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size; public class Troll extends AbstractCreature { public Troll() { - super("Troll", Size.LARGE, Movement.WALKING, Color.DARK); + this(new Mass(4000.0)); + } + + public Troll(Mass mass) { + super("Troll", Size.LARGE, Movement.WALKING, Color.DARK, mass); } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Color.java b/specification/src/main/java/com/iluwatar/specification/property/Color.java index 00f7007ff..6e96b5813 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Color.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Color.java @@ -24,7 +24,7 @@ package com.iluwatar.specification.property; /** - *

Color property.

+ * Color property. */ public enum Color { diff --git a/specification/src/main/java/com/iluwatar/specification/property/Mass.java b/specification/src/main/java/com/iluwatar/specification/property/Mass.java new file mode 100644 index 000000000..6be1edb33 --- /dev/null +++ b/specification/src/main/java/com/iluwatar/specification/property/Mass.java @@ -0,0 +1,65 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.specification.property; + +/** Mass property. */ +public class Mass { + + private double value; + private String title; + + public Mass(double value) { + this.value = value; + this.title = value + "kg"; // Implicit call to Double.toString(value) + } + + public final boolean greaterThan(Mass other) { + return this.value > other.value; + } + + public final boolean smallerThan(Mass other) { + return this.value < other.value; + } + + public final boolean greaterThanOrEq(Mass other) { + return this.value >= other.value; + } + + public final boolean smallerThanOrEq(Mass other) { + return this.value <= other.value; + } + + @Override + public String toString() { + return title; + } + + @Override + public final boolean equals(Object obj) { + if (!(obj instanceof Mass)) { + return false; + } + return ((Mass) obj).value == this.value; + } +} diff --git a/specification/src/main/java/com/iluwatar/specification/selector/MassGreaterThanSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/MassGreaterThanSelector.java new file mode 100644 index 000000000..d613e6a08 --- /dev/null +++ b/specification/src/main/java/com/iluwatar/specification/selector/MassGreaterThanSelector.java @@ -0,0 +1,44 @@ +/* + * 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.specification.selector; + +import com.iluwatar.specification.creature.Creature; +import com.iluwatar.specification.property.Mass; +import java.util.function.Predicate; + +/** Mass selector for values greater than the parameter. */ +public class MassGreaterThanSelector implements Predicate { + + private final Mass mass; + + /** The use of a double as a parameter will spare some typing when instantiating this class. */ + public MassGreaterThanSelector(double mass) { + this.mass = new Mass(mass); + } + + @Override + public boolean test(Creature t) { + return t.getMass().greaterThan(mass); + } +} diff --git a/specification/src/main/java/com/iluwatar/specification/selector/MassSmallerThanOrEqSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/MassSmallerThanOrEqSelector.java new file mode 100644 index 000000000..8a6a9f80e --- /dev/null +++ b/specification/src/main/java/com/iluwatar/specification/selector/MassSmallerThanOrEqSelector.java @@ -0,0 +1,44 @@ +/* + * 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.specification.selector; + +import com.iluwatar.specification.creature.Creature; +import com.iluwatar.specification.property.Mass; +import java.util.function.Predicate; + +/** Mass selector for values smaller or equal to the parameter. */ +public class MassSmallerThanOrEqSelector implements Predicate { + + private final Mass mass; + + /** The use of a double as a parameter will spare some typing when instantiating this class. */ + public MassSmallerThanOrEqSelector(double mass) { + this.mass = new Mass(mass); + } + + @Override + public boolean test(Creature t) { + return t.getMass().smallerThanOrEq(mass); + } +} diff --git a/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java b/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java index 26ff4c1ab..ecd3de27f 100644 --- a/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java +++ b/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java @@ -24,6 +24,7 @@ package com.iluwatar.specification.creature; import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Mass; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Size; import org.junit.jupiter.params.ParameterizedTest; @@ -47,12 +48,12 @@ public class CreatureTest { */ public static Collection dataProvider() { return List.of( - new Object[]{new Dragon(), "Dragon", Size.LARGE, Movement.FLYING, Color.RED}, - new Object[]{new Goblin(), "Goblin", Size.SMALL, Movement.WALKING, Color.GREEN}, - new Object[]{new KillerBee(), "KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT}, - new Object[]{new Octopus(), "Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK}, - new Object[]{new Shark(), "Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT}, - new Object[]{new Troll(), "Troll", Size.LARGE, Movement.WALKING, Color.DARK} + new Object[]{new Dragon(), "Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0)}, + new Object[]{new Goblin(), "Goblin", Size.SMALL, Movement.WALKING, Color.GREEN, new Mass(30.0)}, + new Object[]{new KillerBee(), "KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT, new Mass(6.7)}, + new Object[]{new Octopus(), "Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK, new Mass(12.0)}, + new Object[]{new Shark(), "Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT, new Mass(500.0)}, + new Object[]{new Troll(), "Troll", Size.LARGE, Movement.WALKING, Color.DARK, new Mass(4000.0)} ); } @@ -82,11 +83,17 @@ public class CreatureTest { @ParameterizedTest @MethodSource("dataProvider") - public void testToString(Creature testedCreature, String name, Size size, Movement movement, Color color) { + public void testGetMass(Creature testedCreature, String name, Size size, Movement movement, Color color, Mass mass) { + assertEquals(mass, testedCreature.getMass()); + } + + @ParameterizedTest + @MethodSource("dataProvider") + public void testToString(Creature testedCreature, String name, Size size, Movement movement, Color color, Mass mass) { final String toString = testedCreature.toString(); assertNotNull(toString); assertEquals( - String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color), + String.format("%s [size=%s, movement=%s, color=%s, mass=%s]", name, size, movement, color, mass), toString ); } diff --git a/specification/src/test/java/com/iluwatar/specification/selector/MassSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/MassSelectorTest.java new file mode 100644 index 000000000..eb2f4c5fe --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/MassSelectorTest.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.specification.selector; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.iluwatar.specification.creature.Creature; +import com.iluwatar.specification.property.Mass; +import org.junit.jupiter.api.Test; + +public class MassSelectorTest { + + /** Verify if the mass selector gives the correct results */ + @Test + public void testMass() { + final Creature lightCreature = mock(Creature.class); + when(lightCreature.getMass()).thenReturn(new Mass(50.0)); + + final Creature heavyCreature = mock(Creature.class); + when(heavyCreature.getMass()).thenReturn(new Mass(2500.0)); + + final MassSmallerThanOrEqSelector lightSelector = new MassSmallerThanOrEqSelector(500.0); + assertTrue(lightSelector.test(lightCreature)); + assertFalse(lightSelector.test(heavyCreature)); + } +} From 3ccc9baa1a51bbab763f9fcae7cad22e2bbda8f6 Mon Sep 17 00:00:00 2001 From: Azureyjt Date: Sat, 16 Nov 2019 20:40:23 +0800 Subject: [PATCH 3/4] Game Loop Pattern (#1083) * Add game loop module * Add game loop module * Fix merge issue * Implement game loop module * Implement game loop module * Implement time based game loop * implement VariableStepGameLoop * Implement FixedStepGameLoop * Add UT * Add Unit tests * Fix checkstyle issues * Add README.md * Fix code review issues * Fix code review issues * update README.md --- game-loop/README.md | 41 +++++++ game-loop/pom.xml | 45 ++++++++ .../main/java/com/iluwatar/gameloop/App.java | 76 +++++++++++++ .../java/com/iluwatar/gameloop/Bullet.java | 44 ++++++++ .../iluwatar/gameloop/FixedStepGameLoop.java | 62 +++++++++++ .../iluwatar/gameloop/FrameBasedGameLoop.java | 53 +++++++++ .../com/iluwatar/gameloop/GameController.java | 61 ++++++++++ .../java/com/iluwatar/gameloop/GameLoop.java | 104 ++++++++++++++++++ .../com/iluwatar/gameloop/GameStatus.java | 33 ++++++ .../gameloop/VariableStepGameLoop.java | 51 +++++++++ .../java/com/iluwatar/gameloop/AppTest.java | 39 +++++++ .../gameloop/FixedStepGameLoopTest.java | 54 +++++++++ .../gameloop/FrameBasedGameLoopTest.java | 53 +++++++++ .../iluwatar/gameloop/GameControllerTest.java | 56 ++++++++++ .../com/iluwatar/gameloop/GameLoopTest.java | 71 ++++++++++++ .../gameloop/VariableStepGameLoopTest.java | 54 +++++++++ 16 files changed, 897 insertions(+) create mode 100644 game-loop/README.md create mode 100644 game-loop/pom.xml create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/App.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/Bullet.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/FixedStepGameLoop.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/FrameBasedGameLoop.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/GameController.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/GameStatus.java create mode 100644 game-loop/src/main/java/com/iluwatar/gameloop/VariableStepGameLoop.java create mode 100644 game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java create mode 100644 game-loop/src/test/java/com/iluwatar/gameloop/FixedStepGameLoopTest.java create mode 100644 game-loop/src/test/java/com/iluwatar/gameloop/FrameBasedGameLoopTest.java create mode 100644 game-loop/src/test/java/com/iluwatar/gameloop/GameControllerTest.java create mode 100644 game-loop/src/test/java/com/iluwatar/gameloop/GameLoopTest.java create mode 100644 game-loop/src/test/java/com/iluwatar/gameloop/VariableStepGameLoopTest.java diff --git a/game-loop/README.md b/game-loop/README.md new file mode 100644 index 000000000..424343271 --- /dev/null +++ b/game-loop/README.md @@ -0,0 +1,41 @@ + +--- +layout: pattern +title: Game Loop +folder: game-loop +permalink: /patterns/game-loop/ +categories: Other +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +A game loop runs continuously during gameplay. Each turn of the loop, it processes user input without blocking, updates the game state, and renders the game. It tracks the passage of time to control the rate of gameplay. + +This pattern decouple the progression of game time from user input and processor speed. + +## Applicability +This pattern is used in every game engine. + +## Explanation +Game loop is the main process of all the game rendering threads. It drives input process, internal status update, rendering, AI and all the other processes. + +There are a lot of implementations of game loop: + +- Frame-based game loop + +Frame-based game loop is the easiest implementation. The loop always keeps spinning for the following three processes: processInput, update and render. The problem with it is you have no control over how fast the game runs. On a fast machine, that loop will spin so fast users won’t be able to see what’s going on. On a slow machine, the game will crawl. If you have a part of the game that’s content-heavy or does more AI or physics, the game will actually play slower there. + +- Variable-step game loop + +The variable-step game loop chooses a time step to advance based on how much real time passed since the last frame. The longer the frame takes, the bigger steps the game takes. It always keeps up with real time because it will take bigger and bigger steps to get there. + +- Fixed-step game loop + +For fixed-step game loop, a certain amount of real time has elapsed since the last turn of the game loop. This is how much game time need to be simulated for the game’s “now” to catch up with the player’s. + + +## Credits + +* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html) \ No newline at end of file diff --git a/game-loop/pom.xml b/game-loop/pom.xml new file mode 100644 index 000000000..c28ac04cd --- /dev/null +++ b/game-loop/pom.xml @@ -0,0 +1,45 @@ + + + + + java-design-patterns + com.iluwatar + 1.22.0-SNAPSHOT + + 4.0.0 + + game-loop + + + junit + junit + + + + + \ No newline at end of file diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/App.java b/game-loop/src/main/java/com/iluwatar/gameloop/App.java new file mode 100644 index 000000000..4d9e4c11d --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/App.java @@ -0,0 +1,76 @@ +/* + * 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.gameloop; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A game loop runs continuously during gameplay. Each turn of the loop, it processes + * user input without blocking, updates the game state, and renders the game. It tracks + * the passage of time to control the rate of gameplay. + */ +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Each type of game loop will run for 2 seconds. + */ + private static final int GAME_LOOP_DURATION_TIME = 2000; + + /** + * Program entry point. + * @param args runtime arguments + */ + public static void main(String[] args) { + + try { + LOGGER.info("Start frame-based game loop:"); + var frameBasedGameLoop = new FrameBasedGameLoop(); + frameBasedGameLoop.run(); + Thread.sleep(GAME_LOOP_DURATION_TIME); + frameBasedGameLoop.stop(); + LOGGER.info("Stop frame-based game loop."); + + LOGGER.info("Start variable-step game loop:"); + var variableStepGameLoop = new VariableStepGameLoop(); + variableStepGameLoop.run(); + Thread.sleep(GAME_LOOP_DURATION_TIME); + variableStepGameLoop.stop(); + LOGGER.info("Stop variable-step game loop."); + + LOGGER.info("Start fixed-step game loop:"); + var fixedStepGameLoop = new FixedStepGameLoop(); + fixedStepGameLoop.run(); + Thread.sleep(GAME_LOOP_DURATION_TIME); + fixedStepGameLoop.stop(); + LOGGER.info("Stop variable-step game loop."); + + } catch (InterruptedException e) { + LOGGER.error(e.getMessage()); + } + } + +} diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/Bullet.java b/game-loop/src/main/java/com/iluwatar/gameloop/Bullet.java new file mode 100644 index 000000000..69b93bc74 --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/Bullet.java @@ -0,0 +1,44 @@ +/* + * 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.gameloop; + +/** + * Bullet object class. + */ +public class Bullet { + + private float position; + + public Bullet() { + position = 0.0f; + } + + public float getPosition() { + return position; + } + + public void setPosition(float position) { + this.position = position; + } +} diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/FixedStepGameLoop.java b/game-loop/src/main/java/com/iluwatar/gameloop/FixedStepGameLoop.java new file mode 100644 index 000000000..07ad21584 --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/FixedStepGameLoop.java @@ -0,0 +1,62 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.gameloop; + +/** + * For fixed-step game loop, a certain amount of real time has elapsed since the + * last turn of the game loop. This is how much game time need to be simulated for + * the game’s “now” to catch up with the player’s. + */ +public class FixedStepGameLoop extends GameLoop { + + /** + * 20 ms per frame = 50 FPS. + */ + private static final long MS_PER_FRAME = 20; + + @Override + protected void processGameLoop() { + var previousTime = System.currentTimeMillis(); + var lag = 0L; + while (isGameRunning()) { + var currentTime = System.currentTimeMillis(); + var elapsedTime = currentTime - previousTime; + previousTime = currentTime; + lag += elapsedTime; + + processInput(); + + while (lag >= MS_PER_FRAME) { + update(); + lag -= MS_PER_FRAME; + } + + render(); + } + } + + protected void update() { + controller.moveBullet(0.5f * MS_PER_FRAME / 1000); + } +} diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/FrameBasedGameLoop.java b/game-loop/src/main/java/com/iluwatar/gameloop/FrameBasedGameLoop.java new file mode 100644 index 000000000..45c59168f --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/FrameBasedGameLoop.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.gameloop; + +/** + * Frame-based game loop is the easiest implementation. The loop always keeps spinning + * for the following three processes: processInput, update and render. The problem with + * it is you have no control over how fast the game runs. On a fast machine, that loop + * will spin so fast users won’t be able to see what’s going on. On a slow machine, the + * game will crawl. If you have a part of the game that’s content-heavy or does more AI + * or physics, the game will actually play slower there. + */ +public class FrameBasedGameLoop extends GameLoop { + + @Override + protected void processGameLoop() { + while (isGameRunning()) { + processInput(); + update(); + render(); + } + } + + /** + * Each time when update() is invoked, a new frame is created, and the bullet will be + * moved 0.5f away from the current position. + */ + protected void update() { + controller.moveBullet(0.5f); + } + +} diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/GameController.java b/game-loop/src/main/java/com/iluwatar/gameloop/GameController.java new file mode 100644 index 000000000..00c65332b --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/GameController.java @@ -0,0 +1,61 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.gameloop; + +/** + * Update and render objects in the game. Here we add a Bullet object to the + * game system to show how the game loop works. + */ +public class GameController { + + protected final Bullet bullet; + + /** + * Initialize Bullet instance. + */ + public GameController() { + bullet = new Bullet(); + } + + /** + * Move bullet position by the provided offset. + * + * @param offset moving offset + */ + public void moveBullet(float offset) { + var currentPosition = bullet.getPosition(); + bullet.setPosition(currentPosition + offset); + } + + /** + * Get current position of the bullet. + * + * @return position of bullet + */ + public float getBulletPosition() { + return bullet.getPosition(); + } + +} + diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java b/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java new file mode 100644 index 000000000..474103822 --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java @@ -0,0 +1,104 @@ +/* + * 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.gameloop; + +import java.util.Random; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract class for GameLoop implementation class. + */ +public abstract class GameLoop { + + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + protected volatile GameStatus status; + + protected GameController controller; + + private Thread gameThread; + + /** + * Initialize game status to be stopped. + */ + public GameLoop() { + controller = new GameController(); + status = GameStatus.STOPPED; + } + + /** + * Run game loop. + */ + public void run() { + status = GameStatus.RUNNING; + gameThread = new Thread(() -> processGameLoop()); + gameThread.start(); + } + + /** + * Stop game loop. + */ + public void stop() { + status = GameStatus.STOPPED; + } + + /** + * Check if game is running or not. + * + * @return {@code true} if the game is running. + */ + public boolean isGameRunning() { + return status == GameStatus.RUNNING ? true : false; + } + + /** + * Handle any user input that has happened since the last call. In order to + * simulate the situation in real-life game, here we add a random time lag. + * The time lag ranges from 50 ms to 250 ms. + */ + protected void processInput() { + try { + int lag = new Random().nextInt(200) + 50; + Thread.sleep(lag); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + } + + /** + * Render game frames to screen. Here we print bullet position to simulate + * this process. + */ + protected void render() { + var position = controller.getBulletPosition(); + logger.info("Current bullet position: " + position); + } + + /** + * execute game loop logic. + */ + protected abstract void processGameLoop(); + +} diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/GameStatus.java b/game-loop/src/main/java/com/iluwatar/gameloop/GameStatus.java new file mode 100644 index 000000000..46d90669e --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/GameStatus.java @@ -0,0 +1,33 @@ +/* + * 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.gameloop; + +/** + * Enum class for game status. + */ +public enum GameStatus { + + RUNNING, STOPPED + +} diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/VariableStepGameLoop.java b/game-loop/src/main/java/com/iluwatar/gameloop/VariableStepGameLoop.java new file mode 100644 index 000000000..544590c5b --- /dev/null +++ b/game-loop/src/main/java/com/iluwatar/gameloop/VariableStepGameLoop.java @@ -0,0 +1,51 @@ +/* + * 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.gameloop; + +/** + * The variable-step game loop chooses a time step to advance based on how much + * real time passed since the last frame. The longer the frame takes, the bigger + * steps the game takes. It always keeps up with real time because it will take + * bigger and bigger steps to get there. + */ +public class VariableStepGameLoop extends GameLoop { + + @Override + protected void processGameLoop() { + var lastFrameTime = System.currentTimeMillis(); + while (isGameRunning()) { + processInput(); + var currentFrameTime = System.currentTimeMillis(); + var elapsedTime = currentFrameTime - lastFrameTime; + update(elapsedTime); + lastFrameTime = currentFrameTime; + render(); + } + } + + protected void update(Long elapsedTime) { + controller.moveBullet(0.5f * elapsedTime / 1000); + } + +} diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java new file mode 100644 index 000000000..187fe1537 --- /dev/null +++ b/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.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.gameloop; + +import org.junit.Test; + +/** + * App unit test class. + */ +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + new App().main(args); + } + +} diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/FixedStepGameLoopTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/FixedStepGameLoopTest.java new file mode 100644 index 000000000..5c8614fa1 --- /dev/null +++ b/game-loop/src/test/java/com/iluwatar/gameloop/FixedStepGameLoopTest.java @@ -0,0 +1,54 @@ +/* + * 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.gameloop; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * FixedStepGameLoop unit test class. + */ +public class FixedStepGameLoopTest { + + private FixedStepGameLoop gameLoop; + + @Before + public void setup() { + gameLoop = new FixedStepGameLoop(); + } + + @After + public void tearDown() { + gameLoop = null; + } + + @Test + public void testUpdate() { + gameLoop.update(); + Assert.assertEquals(0.01f, gameLoop.controller.getBulletPosition(), 0); + } + +} diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/FrameBasedGameLoopTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/FrameBasedGameLoopTest.java new file mode 100644 index 000000000..e8eb922dc --- /dev/null +++ b/game-loop/src/test/java/com/iluwatar/gameloop/FrameBasedGameLoopTest.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.gameloop; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * FrameBasedGameLoop unit test class. + */ +public class FrameBasedGameLoopTest { + + private FrameBasedGameLoop gameLoop; + + @Before + public void setup() { + gameLoop = new FrameBasedGameLoop(); + } + + @After + public void tearDown() { + gameLoop = null; + } + + @Test + public void testUpdate() { + gameLoop.update(); + Assert.assertEquals(0.5f, gameLoop.controller.getBulletPosition(), 0); + } +} diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/GameControllerTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/GameControllerTest.java new file mode 100644 index 000000000..3d057d830 --- /dev/null +++ b/game-loop/src/test/java/com/iluwatar/gameloop/GameControllerTest.java @@ -0,0 +1,56 @@ +/* + * 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.gameloop; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class GameControllerTest { + + private GameController controller; + + @Before + public void setup() { + controller = new GameController(); + } + + @After + public void tearDown() { + controller = null; + } + + @Test + public void testMoveBullet() { + controller.moveBullet(1.5f); + Assert.assertEquals(1.5f, controller.bullet.getPosition(), 0); + } + + @Test + public void testGetBulletPosition() { + Assert.assertEquals(controller.bullet.getPosition(), controller.getBulletPosition(), 0); + } + +} diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/GameLoopTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/GameLoopTest.java new file mode 100644 index 000000000..61e2a3bb6 --- /dev/null +++ b/game-loop/src/test/java/com/iluwatar/gameloop/GameLoopTest.java @@ -0,0 +1,71 @@ +/* + * 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.gameloop; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * GameLoop unit test class. + */ +public class GameLoopTest { + + private GameLoop gameLoop; + + /** + * Create mock implementation of GameLoop. + */ + @Before + public void setup() { + gameLoop = new GameLoop() { + @Override + protected void processGameLoop() {} + }; + } + + @After + public void tearDown() { + gameLoop = null; + } + + @Test + public void testRun() { + gameLoop.run(); + Assert.assertEquals(GameStatus.RUNNING, gameLoop.status); + } + + @Test + public void testStop() { + gameLoop.stop(); + Assert.assertEquals(GameStatus.STOPPED, gameLoop.status); + } + + @Test + public void testIsGameRunning() { + Assert.assertEquals(false, gameLoop.isGameRunning()); + } + +} diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/VariableStepGameLoopTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/VariableStepGameLoopTest.java new file mode 100644 index 000000000..43dd4f732 --- /dev/null +++ b/game-loop/src/test/java/com/iluwatar/gameloop/VariableStepGameLoopTest.java @@ -0,0 +1,54 @@ +/* + * 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.gameloop; + +import java.lang.reflect.InvocationTargetException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * VariableStepGameLoop unit test class. + */ +public class VariableStepGameLoopTest { + + private VariableStepGameLoop gameLoop; + + @Before + public void setup() { + gameLoop = new VariableStepGameLoop(); + } + + @After + public void tearDown() { + gameLoop = null; + } + + @Test + public void testUpdate() { + gameLoop.update(20L); + Assert.assertEquals(0.01f, gameLoop.controller.getBulletPosition(), 0); + } +} From 1fdc65054598638e94e2a351873c7e63e95ed4fc Mon Sep 17 00:00:00 2001 From: Anurag Agarwal Date: Sat, 16 Nov 2019 18:18:23 +0530 Subject: [PATCH 4/4] Resolves checkstyle errors for remaining m (#1090) * Reduces checkstyle errors in marker * Reduces checkstyle errors in master-worker-pattern * Reduces checkstyle errors in mediator * Reduces checkstyle errors in memento * Reduces checkstyle errors in model-view-controller * Reduces checkstyle errors in model-view-presenter * Reduces checkstyle errors in module * Reduces checkstyle errors in monad * Reduces checkstyle errors in monostate * Reduces checkstyle errors in multiton * Reduces checkstyle errors in mute-idiom * Reduces checkstyle errors in mutex --- marker/src/main/java/App.java | 30 ++++++------- marker/src/main/java/Guard.java | 2 +- marker/src/main/java/Permission.java | 3 +- marker/src/main/java/Thief.java | 2 +- .../java/com/iluwatar/masterworker/App.java | 45 +++++++++++-------- .../com/iluwatar/masterworker/ArrayInput.java | 9 ++-- .../iluwatar/masterworker/ArrayResult.java | 3 +- .../masterworker/ArrayUtilityMethods.java | 32 ++++++------- .../java/com/iluwatar/masterworker/Input.java | 11 ++--- .../com/iluwatar/masterworker/Result.java | 6 +-- .../system/ArrayTransposeMasterWorker.java | 4 +- .../masterworker/system/MasterWorker.java | 2 +- .../systemmaster/ArrayTransposeMaster.java | 28 ++++++------ .../system/systemmaster/Master.java | 37 ++++++++------- .../systemworkers/ArrayTransposeWorker.java | 16 ++++--- .../system/systemworkers/Worker.java | 7 ++- .../java/com/iluwatar/mediator/Action.java | 10 ++--- .../main/java/com/iluwatar/mediator/App.java | 25 +++++------ .../java/com/iluwatar/mediator/Hobbit.java | 2 - .../java/com/iluwatar/mediator/Hunter.java | 2 - .../java/com/iluwatar/mediator/Party.java | 2 - .../java/com/iluwatar/mediator/PartyImpl.java | 2 - .../com/iluwatar/mediator/PartyMember.java | 2 - .../iluwatar/mediator/PartyMemberBase.java | 2 - .../java/com/iluwatar/mediator/Rogue.java | 2 - .../java/com/iluwatar/mediator/Wizard.java | 2 - .../main/java/com/iluwatar/memento/App.java | 29 ++++++------ .../main/java/com/iluwatar/memento/Star.java | 10 ++--- .../com/iluwatar/memento/StarMemento.java | 2 - .../java/com/iluwatar/memento/StarType.java | 4 +- .../iluwatar/model/view/controller/App.java | 26 +++++------ .../model/view/controller/Fatigue.java | 4 +- .../view/controller/GiantController.java | 2 - .../model/view/controller/GiantModel.java | 4 +- .../model/view/controller/GiantView.java | 4 +- .../model/view/controller/Health.java | 4 +- .../model/view/controller/Nourishment.java | 4 +- .../iluwatar/model/view/presenter/App.java | 32 +++++++------ .../model/view/presenter/FileLoader.java | 17 ++++--- .../view/presenter/FileSelectorJFrame.java | 3 +- .../view/presenter/FileSelectorPresenter.java | 14 +++--- .../view/presenter/FileSelectorStub.java | 14 +++--- .../view/presenter/FileSelectorView.java | 14 ++++-- .../main/java/com/iluwatar/module/App.java | 21 +++++---- .../iluwatar/module/ConsoleLoggerModule.java | 30 ++++++------- .../com/iluwatar/module/FileLoggerModule.java | 36 +++++++-------- .../src/main/java/com/iluwatar/monad/App.java | 28 ++++++------ .../src/main/java/com/iluwatar/monad/Sex.java | 2 +- .../main/java/com/iluwatar/monad/User.java | 4 +- .../java/com/iluwatar/monad/Validator.java | 41 +++++++++-------- .../main/java/com/iluwatar/monostate/App.java | 16 +++---- .../com/iluwatar/monostate/LoadBalancer.java | 9 ++-- .../java/com/iluwatar/monostate/Request.java | 2 - .../java/com/iluwatar/monostate/Server.java | 6 +-- .../main/java/com/iluwatar/multiton/App.java | 20 ++++----- .../java/com/iluwatar/multiton/Nazgul.java | 2 - .../com/iluwatar/multiton/NazgulEnum.java | 5 +-- .../com/iluwatar/multiton/NazgulName.java | 2 - .../src/main/java/com/iluwatar/mute/App.java | 21 ++++----- .../com/iluwatar/mute/CheckedRunnable.java | 2 +- .../src/main/java/com/iluwatar/mute/Mute.java | 27 +++++------ .../main/java/com/iluwatar/mute/Resource.java | 5 +-- .../src/main/java/com/iluwatar/mutex/App.java | 16 +++---- .../src/main/java/com/iluwatar/mutex/Jar.java | 5 +-- .../main/java/com/iluwatar/mutex/Mutex.java | 9 ++-- .../main/java/com/iluwatar/mutex/Thief.java | 13 +++--- 66 files changed, 374 insertions(+), 423 deletions(-) diff --git a/marker/src/main/java/App.java b/marker/src/main/java/App.java index 93697e6eb..384c999dc 100644 --- a/marker/src/main/java/App.java +++ b/marker/src/main/java/App.java @@ -25,27 +25,23 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Created by Alexis on 28-Apr-17. - * With Marker interface idea is to make empty interface and extend it. - * Basically it is just to identify the special objects from normal objects. - * Like in case of serialization , objects that need to be serialized must implement serializable interface - * (it is empty interface) and down the line writeObject() method must be checking - * if it is a instance of serializable or not. - *

- * Marker interface vs annotation - * Marker interfaces and marker annotations both have their uses, - * neither of them is obsolete or always better then the other one. - * If you want to define a type that does not have any new methods associated with it, - * a marker interface is the way to go. - * If you want to mark program elements other than classes and interfaces, - * to allow for the possibility of adding more information to the marker in the future, - * or to fit the marker into a framework that already makes heavy use of annotation types, - * then a marker annotation is the correct choice + * Created by Alexis on 28-Apr-17. With Marker interface idea is to make empty interface and extend + * it. Basically it is just to identify the special objects from normal objects. Like in case of + * serialization , objects that need to be serialized must implement serializable interface (it is + * empty interface) and down the line writeObject() method must be checking if it is a instance of + * serializable or not. + * + *

Marker interface vs annotation Marker interfaces and marker annotations both have their uses, + * neither of them is obsolete or always better then the other one. If you want to define a type + * that does not have any new methods associated with it, a marker interface is the way to go. If + * you want to mark program elements other than classes and interfaces, to allow for the possibility + * of adding more information to the marker in the future, or to fit the marker into a framework + * that already makes heavy use of annotation types, then a marker annotation is the correct choice */ public class App { /** - * Program entry point + * Program entry point. * * @param args command line args */ diff --git a/marker/src/main/java/Guard.java b/marker/src/main/java/Guard.java index d135d5459..9a57e15fd 100644 --- a/marker/src/main/java/Guard.java +++ b/marker/src/main/java/Guard.java @@ -25,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Class defining Guard + * Class defining Guard. */ public class Guard implements Permission { diff --git a/marker/src/main/java/Permission.java b/marker/src/main/java/Permission.java index e1b45e99f..5395ccadf 100644 --- a/marker/src/main/java/Permission.java +++ b/marker/src/main/java/Permission.java @@ -22,8 +22,7 @@ */ /** - * Interface without any methods - * Marker interface is based on that assumption + * Interface without any methods Marker interface is based on that assumption. */ public interface Permission { } diff --git a/marker/src/main/java/Thief.java b/marker/src/main/java/Thief.java index 155a974c1..341eae377 100644 --- a/marker/src/main/java/Thief.java +++ b/marker/src/main/java/Thief.java @@ -25,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Class defining Thief + * Class defining Thief. */ public class Thief { diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/App.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/App.java index e3ffd08c4..b8036b911 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/App.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/App.java @@ -28,30 +28,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - *

The Master-Worker pattern is used when the problem at hand can be solved by dividing into - * multiple parts which need to go through the same computation and may need to be aggregated to get final result. - * Parallel processing is performed using a system consisting of a master and some number of workers, where a - * master divides the work among the workers, gets the result back from them and assimilates all the results to - * give final result. The only communication is between the master and the worker - none of the workers communicate - * among one another and the user only communicates with the master to get required job done.

- *

In our example, we have generic abstract classes {@link MasterWorker}, {@link Master} and {@link Worker} which - * have to be extended by the classes which will perform the specific job at hand (in this case finding transpose of - * matrix, done by {@link ArrayTransposeMasterWorker}, {@link ArrayTransposeMaster} and {@link ArrayTransposeWorker}). - * The Master class divides the work into parts to be given to the workers, collects the results from the workers and - * aggregates it when all workers have responded before returning the solution. The Worker class extends the Thread - * class to enable parallel processing, and does the work once the data has been received from the Master. The - * MasterWorker contains a reference to the Master class, gets the input from the App and passes it on to the Master. - * These 3 classes define the system which computes the result. We also have 2 abstract classes {@link Input} and - * {@link Result}, which contain the input data and result data respectively. The Input class also has an abstract - * method divideData which defines how the data is to be divided into segments. These classes are extended by - * {@link ArrayInput} and {@link ArrayResult}.

+ *

The Master-Worker pattern is used when the problem at hand can be solved by + * dividing into + * multiple parts which need to go through the same computation and may need to be aggregated to get + * final result. Parallel processing is performed using a system consisting of a master and some + * number of workers, where a master divides the work among the workers, gets the result back from + * them and assimilates all the results to give final result. The only communication is between the + * master and the worker - none of the workers communicate among one another and the user only + * communicates with the master to get required job done.

+ *

In our example, we have generic abstract classes {@link MasterWorker}, {@link Master} and + * {@link Worker} which + * have to be extended by the classes which will perform the specific job at hand (in this case + * finding transpose of matrix, done by {@link ArrayTransposeMasterWorker}, {@link + * ArrayTransposeMaster} and {@link ArrayTransposeWorker}). The Master class divides the work into + * parts to be given to the workers, collects the results from the workers and aggregates it when + * all workers have responded before returning the solution. The Worker class extends the Thread + * class to enable parallel processing, and does the work once the data has been received from the + * Master. The MasterWorker contains a reference to the Master class, gets the input from the App + * and passes it on to the Master. These 3 classes define the system which computes the result. We + * also have 2 abstract classes {@link Input} and {@link Result}, which contain the input data and + * result data respectively. The Input class also has an abstract method divideData which defines + * how the data is to be divided into segments. These classes are extended by {@link ArrayInput} and + * {@link ArrayResult}.

*/ public class App { private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point. + * * @param args command line args */ @@ -59,9 +66,9 @@ public class App { ArrayTransposeMasterWorker mw = new ArrayTransposeMasterWorker(); int rows = 10; int columns = 20; - int[][] inputMatrix = ArrayUtilityMethods.createRandomIntMatrix(rows,columns); + int[][] inputMatrix = ArrayUtilityMethods.createRandomIntMatrix(rows, columns); ArrayInput input = new ArrayInput(inputMatrix); - ArrayResult result = (ArrayResult) mw.getResult(input); + ArrayResult result = (ArrayResult) mw.getResult(input); if (result != null) { ArrayUtilityMethods.printMatrix(inputMatrix); ArrayUtilityMethods.printMatrix(result.data); diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayInput.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayInput.java index df2d691b2..cd03a0a21 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayInput.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayInput.java @@ -27,8 +27,7 @@ import java.util.ArrayList; import java.util.Arrays; /** - *Class ArrayInput extends abstract class {@link Input} and contains data - *of type int[][]. + * Class ArrayInput extends abstract class {@link Input} and contains data of type int[][]. */ public class ArrayInput extends Input { @@ -36,7 +35,7 @@ public class ArrayInput extends Input { public ArrayInput(int[][] data) { super(data); } - + static int[] makeDivisions(int[][] data, int num) { int initialDivision = data.length / num; //equally dividing int[] divisions = new int[num]; @@ -81,6 +80,6 @@ public class ArrayInput extends Input { } } return result; - } - } + } + } } diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayResult.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayResult.java index dbe4f7477..a26472e80 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayResult.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayResult.java @@ -24,8 +24,7 @@ package com.iluwatar.masterworker; /** - *Class ArrayResult extends abstract class {@link Result} and contains data - *of type int[][]. + * Class ArrayResult extends abstract class {@link Result} and contains data of type int[][]. */ public class ArrayResult extends Result { diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayUtilityMethods.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayUtilityMethods.java index 04db66492..525bed003 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayUtilityMethods.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/ArrayUtilityMethods.java @@ -23,23 +23,23 @@ package com.iluwatar.masterworker; +import java.util.Random; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Random; - /** - *Class ArrayUtilityMethods has some utility methods for matrices and arrays. + * Class ArrayUtilityMethods has some utility methods for matrices and arrays. */ public class ArrayUtilityMethods { private static final Logger LOGGER = LoggerFactory.getLogger(ArrayUtilityMethods.class); - + private static final Random RANDOM = new Random(); + /** - * Method arraysSame compares 2 arrays @param a1 and @param a2 - * and @return whether their values are equal (boolean). + * Method arraysSame compares 2 arrays @param a1 and @param a2 and @return whether their values + * are equal (boolean). */ public static boolean arraysSame(int[] a1, int[] a2) { @@ -61,10 +61,10 @@ public class ArrayUtilityMethods { } /** - * Method matricesSame compares 2 matrices @param m1 and @param m2 - * and @return whether their values are equal (boolean). + * Method matricesSame compares 2 matrices @param m1 and @param m2 and @return whether their + * values are equal (boolean). */ - + public static boolean matricesSame(int[][] m1, int[][] m2) { if (m1.length != m2.length) { return false; @@ -81,12 +81,12 @@ public class ArrayUtilityMethods { return answer; } } - + /** - * Method createRandomIntMatrix creates a random matrix of size @param rows - * and @param columns @return it (int[][]). + * Method createRandomIntMatrix creates a random matrix of size @param rows and @param columns. + * + * @return it (int[][]). */ - public static int[][] createRandomIntMatrix(int rows, int columns) { int[][] matrix = new int[rows][columns]; for (int i = 0; i < rows; i++) { @@ -97,11 +97,11 @@ public class ArrayUtilityMethods { } return matrix; } - + /** * Method printMatrix prints input matrix @param matrix. */ - + public static void printMatrix(int[][] matrix) { //prints out int[][] for (int i = 0; i < matrix.length; i++) { @@ -111,5 +111,5 @@ public class ArrayUtilityMethods { LOGGER.info(""); } } - + } diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Input.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Input.java index d0d0c2dde..6a957ae80 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Input.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Input.java @@ -26,18 +26,19 @@ package com.iluwatar.masterworker; import java.util.ArrayList; /** - *The abstract Input class, having 1 public field which contains input data, - *and abstract method divideData. + * The abstract Input class, having 1 public field which contains input data, and abstract method + * divideData. + * * @param T will be type of data. */ public abstract class Input { - + public final T data; - + public Input(T data) { this.data = data; } - + public abstract ArrayList divideData(int num); } diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Result.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Result.java index 1bc729aa9..79bc14604 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Result.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/Result.java @@ -24,13 +24,13 @@ package com.iluwatar.masterworker; /** - *The abstract Result class, which contains 1 public field containing result - *data. + * The abstract Result class, which contains 1 public field containing result data. + * * @param T will be type of data. */ public abstract class Result { - + public final T data; public Result(T data) { diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/ArrayTransposeMasterWorker.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/ArrayTransposeMasterWorker.java index 76e9ff35a..86c1a5700 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/ArrayTransposeMasterWorker.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/ArrayTransposeMasterWorker.java @@ -27,8 +27,8 @@ import com.iluwatar.masterworker.system.systemmaster.ArrayTransposeMaster; import com.iluwatar.masterworker.system.systemmaster.Master; /** - *Class ArrayTransposeMasterWorker extends abstract class {@link MasterWorker} and - *specifically solves the problem of finding transpose of input array. + * Class ArrayTransposeMasterWorker extends abstract class {@link MasterWorker} and specifically + * solves the problem of finding transpose of input array. */ public class ArrayTransposeMasterWorker extends MasterWorker { diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/MasterWorker.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/MasterWorker.java index 009faf106..2b16cbf76 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/MasterWorker.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/MasterWorker.java @@ -28,7 +28,7 @@ import com.iluwatar.masterworker.Result; import com.iluwatar.masterworker.system.systemmaster.Master; /** - *The abstract MasterWorker class which contains reference to master. + * The abstract MasterWorker class which contains reference to master. */ public abstract class MasterWorker { diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/ArrayTransposeMaster.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/ArrayTransposeMaster.java index 0a3ab79b7..ffa64572c 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/ArrayTransposeMaster.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/ArrayTransposeMaster.java @@ -23,16 +23,15 @@ package com.iluwatar.masterworker.system.systemmaster; -import java.util.ArrayList; -import java.util.Enumeration; import com.iluwatar.masterworker.ArrayResult; import com.iluwatar.masterworker.system.systemworkers.ArrayTransposeWorker; import com.iluwatar.masterworker.system.systemworkers.Worker; +import java.util.ArrayList; +import java.util.Enumeration; /** - *Class ArrayTransposeMaster extends abstract class {@link Master} and contains - *definition of aggregateData, which will obtain final result from all - *data obtained and for setWorkers. + * Class ArrayTransposeMaster extends abstract class {@link Master} and contains definition of + * aggregateData, which will obtain final result from all data obtained and for setWorkers. */ public class ArrayTransposeMaster extends Master { @@ -43,26 +42,29 @@ public class ArrayTransposeMaster extends Master { @Override ArrayList setWorkers(int num) { ArrayList ws = new ArrayList(num); - for (int i = 0; i < num ; i++) { + for (int i = 0; i < num; i++) { ws.add(new ArrayTransposeWorker(this, i + 1)); //i+1 will be id } return ws; } - + @Override ArrayResult aggregateData() { - //number of rows in final result is number of rows in any of obtained results obtained from workers - int rows = ((ArrayResult) this.getAllResultData().get(this.getAllResultData().keys().nextElement())).data.length; - int columns = 0; //number of columns is sum of number of columns in all results obtained from workers - for (Enumeration e = this.getAllResultData().keys(); e.hasMoreElements();) { + // number of rows in final result is number of rows in any of obtained results from workers + int rows = ((ArrayResult) this.getAllResultData() + .get(this.getAllResultData().keys().nextElement())).data.length; + int columns = + 0; //number of columns is sum of number of columns in all results obtained from workers + for (Enumeration e = this.getAllResultData().keys(); e.hasMoreElements(); ) { columns += ((ArrayResult) this.getAllResultData().get(e.nextElement())).data[0].length; } int[][] resultData = new int[rows][columns]; int columnsDone = 0; //columns aggregated so far for (int i = 0; i < this.getExpectedNumResults(); i++) { //result obtained from ith worker - int[][] work = ((ArrayResult) this.getAllResultData().get(this.getWorkers().get(i).getWorkerId())).data; + int[][] work = + ((ArrayResult) this.getAllResultData().get(this.getWorkers().get(i).getWorkerId())).data; for (int m = 0; m < work.length; m++) { //m = row number, n = columns number for (int n = 0; n < work[0].length; n++) { @@ -73,5 +75,5 @@ public class ArrayTransposeMaster extends Master { } return new ArrayResult(resultData); } - + } diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/Master.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/Master.java index 7e7d796eb..2466df256 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/Master.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemmaster/Master.java @@ -23,18 +23,17 @@ package com.iluwatar.masterworker.system.systemmaster; -import java.util.ArrayList; -import java.util.Hashtable; import com.iluwatar.masterworker.Input; import com.iluwatar.masterworker.Result; import com.iluwatar.masterworker.system.systemworkers.Worker; +import java.util.ArrayList; +import java.util.Hashtable; /** - *The abstract Master class which contains private fields numOfWorkers - *(number of workers), workers (arraylist of workers), expectedNumResults - *(number of divisions of input data, same as expected number of results), - *allResultData (hashtable of results obtained from workers, mapped by - *their ids) and finalResult (aggregated from allResultData). + * The abstract Master class which contains private fields numOfWorkers (number of workers), workers + * (arraylist of workers), expectedNumResults (number of divisions of input data, same as expected + * number of results), allResultData (hashtable of results obtained from workers, mapped by their + * ids) and finalResult (aggregated from allResultData). */ public abstract class Master { @@ -43,7 +42,7 @@ public abstract class Master { private int expectedNumResults; private Hashtable allResultData; private Result finalResult; - + Master(int numOfWorkers) { this.numOfWorkers = numOfWorkers; this.workers = setWorkers(numOfWorkers); @@ -51,46 +50,46 @@ public abstract class Master { this.allResultData = new Hashtable(numOfWorkers); this.finalResult = null; } - + public Result getFinalResult() { return this.finalResult; } - + Hashtable getAllResultData() { return this.allResultData; } - + int getExpectedNumResults() { return this.expectedNumResults; } - + ArrayList getWorkers() { return this.workers; } - + abstract ArrayList setWorkers(int num); - + public void doWork(Input input) { divideWork(input); } - + private void divideWork(Input input) { ArrayList dividedInput = input.divideData(numOfWorkers); if (dividedInput != null) { this.expectedNumResults = dividedInput.size(); - for (int i = 0; i < this.expectedNumResults; i++) { + for (int i = 0; i < this.expectedNumResults; i++) { //ith division given to ith worker in this.workers this.workers.get(i).setReceivedData(this, dividedInput.get(i)); this.workers.get(i).run(); } } } - + public void receiveData(Result data, Worker w) { //check if can receive..if yes: collectResult(data, w.getWorkerId()); } - + private void collectResult(Result data, int workerId) { this.allResultData.put(workerId, data); if (this.allResultData.size() == this.expectedNumResults) { @@ -98,6 +97,6 @@ public abstract class Master { this.finalResult = aggregateData(); } } - + abstract Result aggregateData(); } diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/ArrayTransposeWorker.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/ArrayTransposeWorker.java index 1d06fcc87..37d8ba005 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/ArrayTransposeWorker.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/ArrayTransposeWorker.java @@ -28,8 +28,8 @@ import com.iluwatar.masterworker.ArrayResult; import com.iluwatar.masterworker.system.systemmaster.Master; /** - *Class ArrayTransposeWorker extends abstract class {@link Worker} and defines method - *executeOperation(), to be performed on data received from master. + * Class ArrayTransposeWorker extends abstract class {@link Worker} and defines method + * executeOperation(), to be performed on data received from master. */ public class ArrayTransposeWorker extends Worker { @@ -41,12 +41,14 @@ public class ArrayTransposeWorker extends Worker { @Override ArrayResult executeOperation() { //number of rows in result matrix is equal to number of columns in input matrix and vice versa - int[][] resultData = new int[((ArrayInput) this.getReceivedData()).data[0].length] - [((ArrayInput) this.getReceivedData()).data.length]; - for (int i = 0; i < ((ArrayInput) this.getReceivedData()).data.length; i++) { - for (int j = 0; j < ((ArrayInput) this.getReceivedData()).data[0].length; j++) { + ArrayInput arrayInput = (ArrayInput) this.getReceivedData(); + final int rows = arrayInput.data[0].length; + final int cols = arrayInput.data.length; + int[][] resultData = new int[rows][cols]; + for (int i = 0; i < cols; i++) { + for (int j = 0; j < rows; j++) { //flipping element positions along diagonal - resultData[j][i] = ((ArrayInput) this.getReceivedData()).data[i][j]; + resultData[j][i] = arrayInput.data[i][j]; } } return new ArrayResult(resultData); diff --git a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/Worker.java b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/Worker.java index fff38e953..bfe226ee0 100644 --- a/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/Worker.java +++ b/master-worker-pattern/src/main/java/com/iluwatar/masterworker/system/systemworkers/Worker.java @@ -28,9 +28,8 @@ import com.iluwatar.masterworker.Result; import com.iluwatar.masterworker.system.systemmaster.Master; /** - *The abstract Worker class which extends Thread class to enable parallel - *processing. Contains fields master(holding reference to master), workerId - *(unique id) and receivedData(from master). + * The abstract Worker class which extends Thread class to enable parallel processing. Contains + * fields master(holding reference to master), workerId (unique id) and receivedData(from master). */ public abstract class Worker extends Thread { @@ -61,7 +60,7 @@ public abstract class Worker extends Thread { private void sendToMaster(Result data) { this.master.receiveData(data, this); - } + } public void run() { //from Thread class Result work = executeOperation(); diff --git a/mediator/src/main/java/com/iluwatar/mediator/Action.java b/mediator/src/main/java/com/iluwatar/mediator/Action.java index dc89bcfdc..66e1f42c4 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Action.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Action.java @@ -24,15 +24,15 @@ package com.iluwatar.mediator; /** - * * Action enumeration. - * */ public enum Action { - HUNT("hunted a rabbit", "arrives for dinner"), TALE("tells a tale", "comes to listen"), GOLD( - "found gold", "takes his share of the gold"), ENEMY("spotted enemies", "runs for cover"), NONE( - "", ""); + HUNT("hunted a rabbit", "arrives for dinner"), + TALE("tells a tale", "comes to listen"), + GOLD("found gold", "takes his share of the gold"), + ENEMY("spotted enemies", "runs for cover"), + NONE("", ""); private String title; private String description; diff --git a/mediator/src/main/java/com/iluwatar/mediator/App.java b/mediator/src/main/java/com/iluwatar/mediator/App.java index 9af600f7c..9dbedb4ab 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/App.java +++ b/mediator/src/main/java/com/iluwatar/mediator/App.java @@ -24,32 +24,31 @@ package com.iluwatar.mediator; /** - * * The Mediator pattern defines an object that encapsulates how a set of objects interact. This * pattern is considered to be a behavioral pattern due to the way it can alter the program's * running behavior. - *

- * Usually a program is made up of a large number of classes. So the logic and computation is + * + *

Usually a program is made up of a large number of classes. So the logic and computation is * distributed among these classes. However, as more classes are developed in a program, especially * during maintenance and/or refactoring, the problem of communication between these classes may * become more complex. This makes the program harder to read and maintain. Furthermore, it can * become difficult to change the program, since any change may affect code in several other * classes. - *

- * With the Mediator pattern, communication between objects is encapsulated with a mediator object. - * Objects no longer communicate directly with each other, but instead communicate through the - * mediator. This reduces the dependencies between communicating objects, thereby lowering the + * + *

With the Mediator pattern, communication between objects is encapsulated with a mediator + * object. Objects no longer communicate directly with each other, but instead communicate through + * the mediator. This reduces the dependencies between communicating objects, thereby lowering the * coupling. - *

- * In this example the mediator encapsulates how a set of objects ({@link PartyMember}) interact. - * Instead of referring to each other directly they use the mediator ({@link Party}) interface. - * + * + *

In this example the mediator encapsulates how a set of objects ({@link PartyMember}) + * interact. Instead of referring to each other directly they use the mediator ({@link Party}) + * interface. */ public class App { /** - * Program entry point - * + * Program entry point. + * * @param args command line args */ public static void main(String[] args) { diff --git a/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java b/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java index 1ddec27ab..1e1d53fc3 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java @@ -24,9 +24,7 @@ package com.iluwatar.mediator; /** - * * Hobbit party member. - * */ public class Hobbit extends PartyMemberBase { diff --git a/mediator/src/main/java/com/iluwatar/mediator/Hunter.java b/mediator/src/main/java/com/iluwatar/mediator/Hunter.java index ed73c1684..0711acf70 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Hunter.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Hunter.java @@ -24,9 +24,7 @@ package com.iluwatar.mediator; /** - * * Hunter party member. - * */ public class Hunter extends PartyMemberBase { diff --git a/mediator/src/main/java/com/iluwatar/mediator/Party.java b/mediator/src/main/java/com/iluwatar/mediator/Party.java index c28b063f3..52d91e21d 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Party.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Party.java @@ -24,9 +24,7 @@ package com.iluwatar.mediator; /** - * * Party interface. - * */ public interface Party { diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java b/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java index a2a755408..6384a2187 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java @@ -27,9 +27,7 @@ import java.util.ArrayList; import java.util.List; /** - * * Party implementation. - * */ public class PartyImpl implements Party { diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java index a45b37b17..c233a051a 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java @@ -24,9 +24,7 @@ package com.iluwatar.mediator; /** - * * Interface for party members interacting with {@link Party}. - * */ public interface PartyMember { diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java index 7ff3535e8..2d025db0c 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java @@ -27,9 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * * Abstract base class for party members. - * */ public abstract class PartyMemberBase implements PartyMember { diff --git a/mediator/src/main/java/com/iluwatar/mediator/Rogue.java b/mediator/src/main/java/com/iluwatar/mediator/Rogue.java index 226bd8f04..568510236 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Rogue.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Rogue.java @@ -24,9 +24,7 @@ package com.iluwatar.mediator; /** - * * Rogue party member. - * */ public class Rogue extends PartyMemberBase { diff --git a/mediator/src/main/java/com/iluwatar/mediator/Wizard.java b/mediator/src/main/java/com/iluwatar/mediator/Wizard.java index 33d7d6dce..c138f0265 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Wizard.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Wizard.java @@ -24,9 +24,7 @@ package com.iluwatar.mediator; /** - * * Wizard party member. - * */ public class Wizard extends PartyMemberBase { diff --git a/memento/src/main/java/com/iluwatar/memento/App.java b/memento/src/main/java/com/iluwatar/memento/App.java index fc6dffb06..af57d8d4a 100644 --- a/memento/src/main/java/com/iluwatar/memento/App.java +++ b/memento/src/main/java/com/iluwatar/memento/App.java @@ -23,36 +23,33 @@ package com.iluwatar.memento; +import java.util.Stack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Stack; - /** - * * The Memento pattern is a software design pattern that provides the ability to restore an object * to its previous state (undo via rollback). - *

- * The Memento pattern is implemented with three objects: the originator, a caretaker and a memento. - * The originator is some object that has an internal state. The caretaker is going to do something - * to the originator, but wants to be able to undo the change. The caretaker first asks the - * originator for a memento object. Then it does whatever operation (or sequence of operations) it - * was going to do. To roll back to the state before the operations, it returns the memento object - * to the originator. The memento object itself is an opaque object (one which the caretaker cannot, - * or should not, change). When using this pattern, care should be taken if the originator may - * change other objects or resources - the memento pattern operates on a single object. - *

- * In this example the object ({@link Star}) gives out a "memento" ({@link StarMemento}) that + * + *

The Memento pattern is implemented with three objects: the originator, a caretaker and a + * memento. The originator is some object that has an internal state. The caretaker is going to do + * something to the originator, but wants to be able to undo the change. The caretaker first asks + * the originator for a memento object. Then it does whatever operation (or sequence of operations) + * it was going to do. To roll back to the state before the operations, it returns the memento + * object to the originator. The memento object itself is an opaque object (one which the caretaker + * cannot, or should not, change). When using this pattern, care should be taken if the originator + * may change other objects or resources - the memento pattern operates on a single object. + * + *

In this example the object ({@link Star}) gives out a "memento" ({@link StarMemento}) that * contains the state of the object. Later on the memento can be set back to the object restoring * the state. - * */ public class App { private static final Logger LOGGER = LoggerFactory.getLogger(App.class); /** - * Program entry point + * Program entry point. */ public static void main(String[] args) { Stack states = new Stack<>(); diff --git a/memento/src/main/java/com/iluwatar/memento/Star.java b/memento/src/main/java/com/iluwatar/memento/Star.java index 0e235752e..ebeea28f2 100644 --- a/memento/src/main/java/com/iluwatar/memento/Star.java +++ b/memento/src/main/java/com/iluwatar/memento/Star.java @@ -24,9 +24,7 @@ package com.iluwatar.memento; /** - * * Star uses "mementos" to store and restore state. - * */ public class Star { @@ -35,7 +33,7 @@ public class Star { private int massTons; /** - * Constructor + * Constructor. */ public Star(StarType startType, int startAge, int startMass) { this.type = startType; @@ -44,7 +42,7 @@ public class Star { } /** - * Makes time pass for the star + * Makes time pass for the star. */ public void timePasses() { ageYears *= 2; @@ -96,9 +94,7 @@ public class Star { } /** - * - * StarMemento implementation - * + * StarMemento implementation. */ private static class StarMementoInternal implements StarMemento { diff --git a/memento/src/main/java/com/iluwatar/memento/StarMemento.java b/memento/src/main/java/com/iluwatar/memento/StarMemento.java index b94f5996a..7b7ccd3ec 100644 --- a/memento/src/main/java/com/iluwatar/memento/StarMemento.java +++ b/memento/src/main/java/com/iluwatar/memento/StarMemento.java @@ -24,9 +24,7 @@ package com.iluwatar.memento; /** - * * External interface to memento. - * */ public interface StarMemento { diff --git a/memento/src/main/java/com/iluwatar/memento/StarType.java b/memento/src/main/java/com/iluwatar/memento/StarType.java index 29da62579..507cd506b 100644 --- a/memento/src/main/java/com/iluwatar/memento/StarType.java +++ b/memento/src/main/java/com/iluwatar/memento/StarType.java @@ -24,9 +24,7 @@ package com.iluwatar.memento; /** - * - * StarType enumeration - * + * StarType enumeration. */ public enum StarType { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java index b4bc8f6ca..4607f009d 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java @@ -24,27 +24,25 @@ package com.iluwatar.model.view.controller; /** - * * Model-View-Controller is a pattern for implementing user interfaces. It divides the application * into three interconnected parts namely the model, the view and the controller. - *

- * The central component of MVC, the model, captures the behavior of the application in terms of its - * problem domain, independent of the user interface. The model directly manages the data, logic and - * rules of the application. A view can be any output representation of information, such as a chart - * or a diagram The third part, the controller, accepts input and converts it to commands for the - * model or view. - *

- * In this example we have a giant ({@link GiantModel}) with statuses for health, fatigue and - * nourishment. {@link GiantView} can display the giant with its current status. - * {@link GiantController} receives input affecting the model and delegates redrawing the giant to - * the view. * + *

The central component of MVC, the model, captures the behavior of the application in terms of + * its problem domain, independent of the user interface. The model directly manages the data, logic + * and rules of the application. A view can be any output representation of information, such as a + * chart or a diagram The third part, the controller, accepts input and converts it to commands for + * the model or view. + * + *

In this example we have a giant ({@link GiantModel}) with statuses for health, fatigue and + * nourishment. {@link GiantView} can display the giant with its current status. {@link + * GiantController} receives input affecting the model and delegates redrawing the giant to the + * view. */ public class App { /** - * Program entry point - * + * Program entry point. + * * @param args command line args */ public static void main(String[] args) { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java index 7f0fd2937..b1663df1f 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java @@ -24,9 +24,7 @@ package com.iluwatar.model.view.controller; /** - * - * Fatigue enumeration - * + * Fatigue enumeration. */ public enum Fatigue { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java index e420ec890..e66608117 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java @@ -24,9 +24,7 @@ package com.iluwatar.model.view.controller; /** - * * GiantController can update the giant data and redraw it using the view. - * */ public class GiantController { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java index 4ae2c4c19..c80d0dba0 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java @@ -24,9 +24,7 @@ package com.iluwatar.model.view.controller; /** - * - * GiantModel contains the giant data - * + * GiantModel contains the giant data. */ public class GiantModel { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java index 9590d609d..da14b7c08 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java @@ -27,9 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * - * GiantView displays the giant - * + * GiantView displays the giant. */ public class GiantView { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java index c8b9374bf..30b3b2b90 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java @@ -24,9 +24,7 @@ package com.iluwatar.model.view.controller; /** - * - * Health enumeration - * + * Health enumeration. */ public enum Health { diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java index 9810b2015..3ced564cc 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java @@ -24,9 +24,7 @@ package com.iluwatar.model.view.controller; /** - * - * Nourishment enumeration - * + * Nourishment enumeration. */ public enum Nourishment { diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java index 9b6e6cde8..43984e847 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java @@ -24,31 +24,29 @@ package com.iluwatar.model.view.presenter; /** - * - * The Model-View-Presenter(MVP) architectural pattern, helps us achieve what is called - * "The separation of concerns" principle. This is accomplished by separating the application's - * logic (Model), GUIs (View), and finally the way that the user's actions update the application's - * logic (Presenter). - *

- * In the following example, The {@link FileLoader} class represents the app's logic, the - * {@link FileSelectorJFrame} is the GUI and the {@link FileSelectorPresenter} is responsible to - * respond to users' actions. - *

- * Finally, please notice the wiring between the Presenter and the View and between the Presenter - * and the Model. - * + * The Model-View-Presenter(MVP) architectural pattern, helps us achieve what is called "The + * separation of concerns" principle. This is accomplished by separating the application's logic + * (Model), GUIs (View), and finally the way that the user's actions update the application's logic + * (Presenter). + * + *

In the following example, The {@link FileLoader} class represents the app's logic, the {@link + * FileSelectorJFrame} is the GUI and the {@link FileSelectorPresenter} is responsible to respond to + * users' actions. + * + *

Finally, please notice the wiring between the Presenter and the View and between the + * Presenter and the Model. */ public class App { /** - * Program entry point - * + * Program entry point. + * * @param args command line args */ public static void main(String[] args) { FileLoader loader = new FileLoader(); - FileSelectorJFrame jFrame = new FileSelectorJFrame(); - FileSelectorPresenter presenter = new FileSelectorPresenter(jFrame); + FileSelectorJFrame frame = new FileSelectorJFrame(); + FileSelectorPresenter presenter = new FileSelectorPresenter(frame); presenter.setLoader(loader); presenter.start(); } diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java index 980c0b56c..9c01b2044 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java @@ -27,23 +27,22 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.Serializable; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Every instance of this class represents the Model component in the Model-View-Presenter * architectural pattern. - *

- * It is responsible for reading and loading the contents of a given file. + * + *

It is responsible for reading and loading the contents of a given file. */ public class FileLoader implements Serializable { /** - * Generated serial version UID + * Generated serial version UID. */ private static final long serialVersionUID = -4745803872902019069L; - + private static final Logger LOGGER = LoggerFactory.getLogger(FileLoader.class); /** @@ -81,7 +80,7 @@ public class FileLoader implements Serializable { /** * Sets the path of the file to be loaded, to the given value. - * + * * @param fileName The path of the file to be loaded. */ public void setFileName(String fileName) { @@ -89,6 +88,8 @@ public class FileLoader implements Serializable { } /** + * Gets the path of the file to be loaded. + * * @return fileName The path of the file to be loaded. */ public String getFileName() { @@ -96,6 +97,8 @@ public class FileLoader implements Serializable { } /** + * Returns true if the given file exists. + * * @return True, if the file given exists, false otherwise. */ public boolean fileExists() { @@ -103,6 +106,8 @@ public class FileLoader implements Serializable { } /** + * Returns true if the given file is loaded. + * * @return True, if the file is loaded, false otherwise. */ public boolean isLoaded() { diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java index 3d9bc035a..77523ccaa 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java @@ -26,7 +26,6 @@ package com.iluwatar.model.view.presenter; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; - import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; @@ -82,7 +81,7 @@ public class FileSelectorJFrame extends JFrame implements FileSelectorView, Acti private JPanel panel; /** - * The Presenter component that the frame will interact with + * The Presenter component that the frame will interact with. */ private FileSelectorPresenter presenter; diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java index a9cf1ba80..35e1c0076 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java @@ -28,13 +28,13 @@ import java.io.Serializable; /** * Every instance of this class represents the Presenter component in the Model-View-Presenter * architectural pattern. - *

- * It is responsible for reacting to the user's actions and update the View component. + * + *

It is responsible for reacting to the user's actions and update the View component. */ public class FileSelectorPresenter implements Serializable { /** - * Generated serial version UID + * Generated serial version UID. */ private static final long serialVersionUID = 1210314339075855074L; @@ -49,8 +49,8 @@ public class FileSelectorPresenter implements Serializable { private FileLoader loader; /** - * Constructor - * + * Constructor. + * * @param view The view component that the presenter will interact with. */ public FileSelectorPresenter(FileSelectorView view) { @@ -59,7 +59,7 @@ public class FileSelectorPresenter implements Serializable { /** * Sets the {@link FileLoader} object, to the value given as parameter. - * + * * @param loader The new {@link FileLoader} object(the Model component). */ public void setLoader(FileLoader loader) { @@ -82,7 +82,7 @@ public class FileSelectorPresenter implements Serializable { } /** - * Ok button handler + * Ok button handler. */ public void confirmed() { if (loader.getFileName() == null || loader.getFileName().equals("")) { diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java index d034bcb04..1d3aa07fb 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java @@ -26,12 +26,12 @@ package com.iluwatar.model.view.presenter; /** * Every instance of this class represents the Stub component in the Model-View-Presenter * architectural pattern. - *

- * The stub implements the View interface and it is useful when we want the test the reaction to + * + *

The stub implements the View interface and it is useful when we want the test the reaction to * user events, such as mouse clicks. - *

- * Since we can not test the GUI directly, the MVP pattern provides this functionality through the - * View's dummy implementation, the Stub. + * + *

Since we can not test the GUI directly, the MVP pattern provides this functionality through + * the View's dummy implementation, the Stub. */ public class FileSelectorStub implements FileSelectorView { @@ -61,7 +61,7 @@ public class FileSelectorStub implements FileSelectorView { private boolean dataDisplayed; /** - * Constructor + * Constructor. */ public FileSelectorStub() { this.opened = false; @@ -124,6 +124,8 @@ public class FileSelectorStub implements FileSelectorView { } /** + * Returns true, if the data were displayed. + * * @return True if the data where displayed, false otherwise. */ public boolean dataDisplayed() { diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java index 3deec63d9..e381784c5 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java @@ -42,44 +42,50 @@ public interface FileSelectorView extends Serializable { void close(); /** + * Returns true if view is opened. + * * @return True, if the view is opened, false otherwise. */ boolean isOpened(); /** * Sets the presenter component, to the one given as parameter. - * + * * @param presenter The new presenter component. */ void setPresenter(FileSelectorPresenter presenter); /** + * Gets presenter component. + * * @return The presenter Component. */ FileSelectorPresenter getPresenter(); /** * Sets the file's name, to the value given as parameter. - * + * * @param name The new name of the file. */ void setFileName(String name); /** + * Gets the name of file. + * * @return The name of the file. */ String getFileName(); /** * Displays a message to the users. - * + * * @param message The message to be displayed. */ void showMessage(String message); /** * Displays the data to the view. - * + * * @param data The data to be written. */ void displayData(String data); diff --git a/module/src/main/java/com/iluwatar/module/App.java b/module/src/main/java/com/iluwatar/module/App.java index af0432afb..1b6cbbd23 100644 --- a/module/src/main/java/com/iluwatar/module/App.java +++ b/module/src/main/java/com/iluwatar/module/App.java @@ -31,10 +31,9 @@ import java.io.FileNotFoundException; * An object that applies this pattern can provide the equivalent of a namespace, providing the * initialization and finalization process of a static class or a class with static members with * cleaner, more concise syntax and semantics. - *

- * The below example demonstrates a use case for testing two different modules: File Logger and + * + *

The below example demonstrates a use case for testing two different modules: File Logger and * Console Logger - * */ public class App { @@ -42,10 +41,10 @@ public class App { public static ConsoleLoggerModule consoleLoggerModule; /** - * Following method performs the initialization - * + * Following method performs the initialization. + * * @throws FileNotFoundException if program is not able to find log files (output.txt and - * error.txt) + * error.txt) */ public static void prepare() throws FileNotFoundException { @@ -55,7 +54,7 @@ public class App { } /** - * Following method performs the finalization + * Following method performs the finalization. */ public static void unprepare() { @@ -65,8 +64,8 @@ public class App { } /** - * Following method is main executor - * + * Following method is main executor. + * * @param args for providing default program arguments */ public static void execute(final String... args) { @@ -82,10 +81,10 @@ public class App { /** * Program entry point. - * + * * @param args command line args. * @throws FileNotFoundException if program is not able to find log files (output.txt and - * error.txt) + * error.txt) */ public static void main(final String... args) throws FileNotFoundException { prepare(); diff --git a/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java b/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java index 6e6d0539d..7ca0d873d 100644 --- a/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java +++ b/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java @@ -23,16 +23,15 @@ package com.iluwatar.module; +import java.io.PrintStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.PrintStream; - /** - * The ConsoleLoggerModule is responsible for showing logs on System Console - *

- * The below example demonstrates a Console logger module, which can print simple and error messages - * in two designated formats + * The ConsoleLoggerModule is responsible for showing logs on System Console. + * + *

The below example demonstrates a Console logger module, which can print simple and error + * messages in two designated formats */ public final class ConsoleLoggerModule { @@ -43,11 +42,12 @@ public final class ConsoleLoggerModule { public PrintStream output = null; public PrintStream error = null; - private ConsoleLoggerModule() {} + private ConsoleLoggerModule() { + } /** - * Static method to get single instance of class - * + * Static method to get single instance of class. + * * @return singleton instance of ConsoleLoggerModule */ public static ConsoleLoggerModule getSingleton() { @@ -60,7 +60,7 @@ public final class ConsoleLoggerModule { } /** - * Following method performs the initialization + * Following method performs the initialization. */ public ConsoleLoggerModule prepare() { @@ -73,7 +73,7 @@ public final class ConsoleLoggerModule { } /** - * Following method performs the finalization + * Following method performs the finalization. */ public void unprepare() { @@ -93,8 +93,8 @@ public final class ConsoleLoggerModule { } /** - * Used to print a message - * + * Used to print a message. + * * @param value will be printed on console */ public void printString(final String value) { @@ -102,8 +102,8 @@ public final class ConsoleLoggerModule { } /** - * Used to print a error message - * + * Used to print a error message. + * * @param value will be printed on error console */ public void printErrorString(final String value) { diff --git a/module/src/main/java/com/iluwatar/module/FileLoggerModule.java b/module/src/main/java/com/iluwatar/module/FileLoggerModule.java index e461b31d7..185bd0ae2 100644 --- a/module/src/main/java/com/iluwatar/module/FileLoggerModule.java +++ b/module/src/main/java/com/iluwatar/module/FileLoggerModule.java @@ -23,18 +23,17 @@ package com.iluwatar.module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * The FileLoggerModule is responsible for showing logs on File System - *

- * The below example demonstrates a File logger module, which can print simple and error messages in - * two designated files + * The FileLoggerModule is responsible for showing logs on File System. + * + *

The below example demonstrates a File logger module, which can print simple and error + * messages in two designated files */ public final class FileLoggerModule { @@ -48,11 +47,12 @@ public final class FileLoggerModule { public PrintStream output = null; public PrintStream error = null; - private FileLoggerModule() {} + private FileLoggerModule() { + } /** - * Static method to get single instance of class - * + * Static method to get single instance of class. + * * @return singleton instance of FileLoggerModule */ public static FileLoggerModule getSingleton() { @@ -65,10 +65,10 @@ public final class FileLoggerModule { } /** - * Following method performs the initialization - * + * Following method performs the initialization. + * * @throws FileNotFoundException if program is not able to find log files (output.txt and - * error.txt) + * error.txt) */ public FileLoggerModule prepare() throws FileNotFoundException { @@ -81,7 +81,7 @@ public final class FileLoggerModule { } /** - * Following method performs the finalization + * Following method performs the finalization. */ public void unprepare() { @@ -101,8 +101,8 @@ public final class FileLoggerModule { } /** - * Used to print a message - * + * Used to print a message. + * * @param value will be printed in file */ public void printString(final String value) { @@ -110,8 +110,8 @@ public final class FileLoggerModule { } /** - * Used to print a error message - * + * Used to print a error message. + * * @param value will be printed on error file */ public void printErrorString(final String value) { diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java index 94e465487..ccb42edd0 100644 --- a/monad/src/main/java/com/iluwatar/monad/App.java +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -23,26 +23,27 @@ package com.iluwatar.monad; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * The Monad pattern defines a monad structure, that enables chaining operations - * in pipelines and processing data step by step. - * Formally, monad consists of a type constructor M and two operations: + * The Monad pattern defines a monad structure, that enables chaining operations in pipelines and + * processing data step by step. Formally, monad consists of a type constructor M and two + * operations: *
bind - that takes monadic object and a function from plain object to the * monadic value and returns monadic value. *
return - that takes plain type object and returns this object wrapped in a monadic value. - *

- * In the given example, the Monad pattern is represented as a {@link Validator} that takes an instance - * of a plain object with {@link Validator#of(Object)} - * and validates it {@link Validator#validate(Function, Predicate, String)} against given predicates. - *

As a validation result {@link Validator#get()} it either returns valid object {@link Validator#t} - * or throws a list of exceptions {@link Validator#exceptions} collected during validation. + * + *

In the given example, the Monad pattern is represented as a {@link Validator} that takes an + * instance of a plain object with {@link Validator#of(Object)} and validates it {@link + * Validator#validate(Function, Predicate, String)} against given predicates. + * + *

As a validation result {@link Validator#get()} it either returns valid object {@link + * Validator#t} or throws a list of exceptions {@link Validator#exceptions} collected during + * validation. */ public class App { @@ -58,6 +59,7 @@ public class App { LOGGER.info(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") .validate(User::getName, name -> !name.isEmpty(), "name is empty") .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'") - .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString()); + .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get() + .toString()); } } diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java index 960711656..cc772c340 100644 --- a/monad/src/main/java/com/iluwatar/monad/Sex.java +++ b/monad/src/main/java/com/iluwatar/monad/Sex.java @@ -24,7 +24,7 @@ package com.iluwatar.monad; /** - * Enumeration of Types of Sex + * Enumeration of Types of Sex. */ public enum Sex { MALE, FEMALE diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java index 9ecaa5275..77766d1aa 100644 --- a/monad/src/main/java/com/iluwatar/monad/User.java +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -24,7 +24,7 @@ package com.iluwatar.monad; /** - * User Definition + * User Definition. */ public class User { @@ -34,6 +34,8 @@ public class User { private String email; /** + * Constructor. + * * @param name - name * @param age - age * @param sex - sex diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java index b5618f91c..2d1f1bdab 100644 --- a/monad/src/main/java/com/iluwatar/monad/Validator.java +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -30,18 +30,18 @@ import java.util.function.Function; import java.util.function.Predicate; /** - * Class representing Monad design pattern. Monad is a way of chaining operations on the - * given object together step by step. In Validator each step results in either success or - * failure indicator, giving a way of receiving each of them easily and finally getting - * validated object or list of exceptions. + * Class representing Monad design pattern. Monad is a way of chaining operations on the given + * object together step by step. In Validator each step results in either success or failure + * indicator, giving a way of receiving each of them easily and finally getting validated object or + * list of exceptions. * * @param Placeholder for an object. */ public class Validator { /** - * Object that is validated + * Object that is validated. */ - private final T t; + private final T obj; /** * List of exception thrown during validation. @@ -50,14 +50,15 @@ public class Validator { /** * Creates a monadic value of given object. - * @param t object to be validated + * + * @param obj object to be validated */ - private Validator(T t) { - this.t = t; + private Validator(T obj) { + this.obj = obj; } /** - * Creates validator against given object + * Creates validator against given object. * * @param t object to be validated * @param object's type @@ -68,25 +69,27 @@ public class Validator { } /** - * @param validation one argument boolean-valued function that - * represents one step of validation. Adds exception to main validation exception - * list when single step validation ends with failure. + * Checks if the validation is successful. + * + * @param validation one argument boolean-valued function that represents one step of validation. + * Adds exception to main validation exception list when single step validation + * ends with failure. * @param message error message when object is invalid * @return this */ public Validator validate(Predicate validation, String message) { - if (!validation.test(t)) { + if (!validation.test(obj)) { exceptions.add(new IllegalStateException(message)); } return this; } /** - * Extension for the {@link Validator#validate(Function, Predicate, String)} method, - * dedicated for objects, that need to be projected before requested validation. + * Extension for the {@link Validator#validate(Function, Predicate, String)} method, dedicated for + * objects, that need to be projected before requested validation. * - * @param projection function that gets an objects, and returns projection representing - * element to be validated. + * @param projection function that gets an objects, and returns projection representing element to + * be validated. * @param validation see {@link Validator#validate(Function, Predicate, String)} * @param message see {@link Validator#validate(Function, Predicate, String)} * @param see {@link Validator#validate(Function, Predicate, String)} @@ -105,7 +108,7 @@ public class Validator { */ public T get() throws IllegalStateException { if (exceptions.isEmpty()) { - return t; + return obj; } IllegalStateException e = new IllegalStateException(); exceptions.forEach(e::addSuppressed); diff --git a/monostate/src/main/java/com/iluwatar/monostate/App.java b/monostate/src/main/java/com/iluwatar/monostate/App.java index 4b6f8b14b..64cb38461 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/App.java +++ b/monostate/src/main/java/com/iluwatar/monostate/App.java @@ -24,28 +24,22 @@ package com.iluwatar.monostate; - /** - * * The MonoState pattern ensures that all instances of the class will have the same state. This can * be used a direct replacement of the Singleton pattern. - * - *

- * In the following example, The {@link LoadBalancer} class represents the app's logic. It contains - * a series of Servers, which can handle requests of type {@link Request}. Two instances of + * + *

In the following example, The {@link LoadBalancer} class represents the app's logic. It + * contains a series of Servers, which can handle requests of type {@link Request}. Two instances of * LoadBalacer are created. When a request is made to a server via the first LoadBalancer the state * change in the first load balancer affects the second. So if the first LoadBalancer selects the * Server 1, the second LoadBalancer on a new request will select the Second server. If a third * LoadBalancer is created and a new request is made to it, then it will select the third server as * the second load balancer has already selected the second server. - *

- * . - * */ public class App { /** - * Program entry point - * + * Program entry point. + * * @param args command line args */ public static void main(String[] args) { diff --git a/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java index ae590be5e..8546ae177 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java +++ b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java @@ -31,7 +31,6 @@ import java.util.List; * receiving a new Request, it delegates the call to the servers in a Round Robin Fashion. Since all * instances of the class share the same state, all instances will delegate to the same server on * receiving a new Request. - * */ public class LoadBalancer { @@ -40,13 +39,13 @@ public class LoadBalancer { static { int id = 0; - for (int port : new int[] {8080, 8081, 8082, 8083, 8084}) { + for (int port : new int[]{8080, 8081, 8082, 8083, 8084}) { SERVERS.add(new Server("localhost", port, ++id)); } } /** - * Add new server + * Add new server. */ public final void addServer(Server server) { synchronized (SERVERS) { @@ -64,7 +63,7 @@ public class LoadBalancer { } /** - * Handle request + * Handle request. */ public synchronized void serverRequest(Request request) { if (lastServedId >= SERVERS.size()) { @@ -73,5 +72,5 @@ public class LoadBalancer { Server server = SERVERS.get(lastServedId++); server.serve(request); } - + } diff --git a/monostate/src/main/java/com/iluwatar/monostate/Request.java b/monostate/src/main/java/com/iluwatar/monostate/Request.java index 5a7429998..d7e4fcf8f 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/Request.java +++ b/monostate/src/main/java/com/iluwatar/monostate/Request.java @@ -24,9 +24,7 @@ package com.iluwatar.monostate; /** - * * The Request class. A {@link Server} can handle an instance of a Request. - * */ public class Request { diff --git a/monostate/src/main/java/com/iluwatar/monostate/Server.java b/monostate/src/main/java/com/iluwatar/monostate/Server.java index fa809864c..cd08b2b29 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/Server.java +++ b/monostate/src/main/java/com/iluwatar/monostate/Server.java @@ -27,10 +27,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the servers * in a simplistic Round Robin fashion. - * */ public class Server { @@ -41,7 +39,7 @@ public class Server { public final int id; /** - * Constructor + * Constructor. */ public Server(String host, int port, int id) { this.host = host; @@ -59,6 +57,6 @@ public class Server { public void serve(Request request) { LOGGER.info("Server ID {} associated to host : {} and port {}. Processed request with value {}", - id, host, port, request.value); + id, host, port, request.value); } } diff --git a/multiton/src/main/java/com/iluwatar/multiton/App.java b/multiton/src/main/java/com/iluwatar/multiton/App.java index 8fb226625..eb3e0313f 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/App.java +++ b/multiton/src/main/java/com/iluwatar/multiton/App.java @@ -27,26 +27,24 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * * Whereas Singleton design pattern introduces single globally accessible object the Multiton * pattern defines many globally accessible objects. The client asks for the correct instance from * the Multiton by passing an enumeration as parameter. - *

- * There is more than one way to implement the multiton design pattern. In the first example - * {@link Nazgul} is the Multiton and we can ask single {@link Nazgul} from it using {@link NazgulName}. - * The {@link Nazgul}s are statically initialized and stored in concurrent hash map. - *

- * In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable because - * of the way java supports enums. * + *

There is more than one way to implement the multiton design pattern. In the first example + * {@link Nazgul} is the Multiton and we can ask single {@link Nazgul} from it using {@link + * NazgulName}. The {@link Nazgul}s are statically initialized and stored in concurrent hash map. + * + *

In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable + * because of the way java supports enums. */ public class App { private static final Logger LOGGER = LoggerFactory.getLogger(App.class); /** - * Program entry point - * + * Program entry point. + * * @param args command line args */ public static void main(String[] args) { @@ -60,7 +58,7 @@ public class App { LOGGER.info("ADUNAPHEL={}", Nazgul.getInstance(NazgulName.ADUNAPHEL)); LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN)); LOGGER.info("UVATHA={}", Nazgul.getInstance(NazgulName.UVATHA)); - + // enum multiton LOGGER.info("KHAMUL={}", NazgulEnum.KHAMUL); LOGGER.info("MURAZOR={}", NazgulEnum.MURAZOR); diff --git a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java index 52a25e00d..f55f85aca 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java +++ b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java @@ -27,9 +27,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * * Nazgul is a Multiton class. Nazgul instances can be queried using {@link #getInstance} method. - * */ public final class Nazgul { diff --git a/multiton/src/main/java/com/iluwatar/multiton/NazgulEnum.java b/multiton/src/main/java/com/iluwatar/multiton/NazgulEnum.java index f119ee68f..5b5c48d66 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/NazgulEnum.java +++ b/multiton/src/main/java/com/iluwatar/multiton/NazgulEnum.java @@ -24,11 +24,10 @@ package com.iluwatar.multiton; /** - * enum based multiton implementation - * + * enum based multiton implementation. */ public enum NazgulEnum { - + KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA; } diff --git a/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java b/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java index 5fe2a5a0f..c7865dceb 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java +++ b/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java @@ -24,9 +24,7 @@ package com.iluwatar.multiton; /** - * * Each Nazgul has different {@link NazgulName}. - * */ public enum NazgulName { diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/App.java b/mute-idiom/src/main/java/com/iluwatar/mute/App.java index 28649e249..d4f140bf0 100644 --- a/mute-idiom/src/main/java/com/iluwatar/mute/App.java +++ b/mute-idiom/src/main/java/com/iluwatar/mute/App.java @@ -23,19 +23,17 @@ package com.iluwatar.mute; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.sql.SQLException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Mute pattern is utilized when we need to suppress an exception due to an API flaw or in - * situation when all we can do to handle the exception is to log it. - * This pattern should not be used everywhere. It is very important to logically handle the - * exceptions in a system, but some situations like the ones described above require this pattern, - * so that we don't need to repeat + * Mute pattern is utilized when we need to suppress an exception due to an API flaw or in situation + * when all we can do to handle the exception is to log it. This pattern should not be used + * everywhere. It is very important to logically handle the exceptions in a system, but some + * situations like the ones described above require this pattern, so that we don't need to repeat *

  * 
  *   try {
@@ -45,7 +43,6 @@ import java.sql.SQLException;
  *   }
  * 
  * 
every time we need to ignore an exception. - * */ public class App { @@ -53,7 +50,7 @@ public class App { /** * Program entry point. - * + * * @param args command line args. * @throws Exception if any exception occurs */ @@ -65,7 +62,7 @@ public class App { } /* - * Typically used when the API declares some exception but cannot do so. Usually a + * Typically used when the API declares some exception but cannot do so. Usually a * signature mistake.In this example out is not supposed to throw exception as it is a * ByteArrayOutputStream. So we utilize mute, which will throw AssertionError if unexpected * exception occurs. @@ -98,7 +95,7 @@ public class App { private static Resource acquireResource() throws SQLException { return new Resource() { - + @Override public void close() throws IOException { throw new IOException("Error in closing resource: " + this); diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java b/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java index d5fdaaec2..f15153d00 100644 --- a/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java +++ b/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java @@ -25,12 +25,12 @@ package com.iluwatar.mute; /** * A runnable which may throw exception on execution. - * */ @FunctionalInterface public interface CheckedRunnable { /** * Same as {@link Runnable#run()} with a possibility of exception in execution. + * * @throws Exception if any exception occurs. */ void run() throws Exception; diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java b/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java index 87a1f651e..30e1698d5 100644 --- a/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java +++ b/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java @@ -30,17 +30,18 @@ import java.io.IOException; * A utility class that allows you to utilize mute idiom. */ public final class Mute { - + // The constructor is never meant to be called. - private Mute() {} + private Mute() { + } /** - * Executes the runnable and throws the exception occurred within a {@link AssertionError}. - * This method should be utilized to mute the operations that are guaranteed not to throw an exception. - * For instance {@link ByteArrayOutputStream#write(byte[])} declares in it's signature that it can throw - * an {@link IOException}, but in reality it cannot. This is because the bulk write method is not overridden - * in {@link ByteArrayOutputStream}. - * + * Executes the runnable and throws the exception occurred within a {@link + * AssertionError}. This method should be utilized to mute the operations that are guaranteed not + * to throw an exception. For instance {@link ByteArrayOutputStream#write(byte[])} declares in + * it's signature that it can throw an {@link IOException}, but in reality it cannot. This is + * because the bulk write method is not overridden in {@link ByteArrayOutputStream}. + * * @param runnable a runnable that should never throw an exception on execution. */ public static void mute(CheckedRunnable runnable) { @@ -52,11 +53,11 @@ public final class Mute { } /** - * Executes the runnable and logs the exception occurred on {@link System#err}. - * This method should be utilized to mute the operations about which most you can do is log. - * For instance while closing a connection to database, or cleaning up a resource, - * all you can do is log the exception occurred. - * + * Executes the runnable and logs the exception occurred on {@link System#err}. This + * method should be utilized to mute the operations about which most you can do is log. For + * instance while closing a connection to database, or cleaning up a resource, all you can do is + * log the exception occurred. + * * @param runnable a runnable that may throw an exception on execution. */ public static void loggedMute(CheckedRunnable runnable) { diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java b/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java index a10fe4617..e56025ce4 100644 --- a/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java +++ b/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java @@ -26,9 +26,8 @@ package com.iluwatar.mute; import java.io.Closeable; /** - * Represents any resource that the application might acquire and that must be closed - * after it is utilized. Example of such resources can be a database connection, open - * files, sockets. + * Represents any resource that the application might acquire and that must be closed after it is + * utilized. Example of such resources can be a database connection, open files, sockets. */ public interface Resource extends Closeable { diff --git a/mutex/src/main/java/com/iluwatar/mutex/App.java b/mutex/src/main/java/com/iluwatar/mutex/App.java index 827307d0f..e4a952ef9 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/App.java +++ b/mutex/src/main/java/com/iluwatar/mutex/App.java @@ -25,19 +25,17 @@ package com.iluwatar.mutex; /** * A Mutex prevents multiple threads from accessing a resource simultaneously. - *

- * In this example we have two thieves who are taking beans from a jar. - * Only one thief can take a bean at a time. This is ensured by a Mutex lock - * which must be acquired in order to access the jar. Each thief attempts to - * acquire the lock, take a bean and then release the lock. If the lock has - * already been acquired, the thief will be prevented from continuing (blocked) - * until the lock has been released. The thieves stop taking beans once there - * are no beans left to take. + * + *

In this example we have two thieves who are taking beans from a jar. Only one thief can take + * a bean at a time. This is ensured by a Mutex lock which must be acquired in order to access the + * jar. Each thief attempts to acquire the lock, take a bean and then release the lock. If the lock + * has already been acquired, the thief will be prevented from continuing (blocked) until the lock + * has been released. The thieves stop taking beans once there are no beans left to take. */ public class App { /** - * main method + * main method. */ public static void main(String[] args) { Mutex mutex = new Mutex(); diff --git a/mutex/src/main/java/com/iluwatar/mutex/Jar.java b/mutex/src/main/java/com/iluwatar/mutex/Jar.java index 427907f31..f68b266ad 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Jar.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Jar.java @@ -24,9 +24,8 @@ package com.iluwatar.mutex; /** - * A Jar has a resource of beans which can only be accessed by a single Thief - * (thread) at any one time. A Mutex lock is used to prevent more than one Thief - * taking a bean simultaneously. + * A Jar has a resource of beans which can only be accessed by a single Thief (thread) at any one + * time. A Mutex lock is used to prevent more than one Thief taking a bean simultaneously. */ public class Jar { diff --git a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java index a2ef71f11..6c62cc8ea 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java @@ -34,16 +34,15 @@ public class Mutex implements Lock { private Object owner; /** - * Returns the current owner of the Mutex, or null if available + * Returns the current owner of the Mutex, or null if available. */ public Object getOwner() { return owner; } - + /** - * Method called by a thread to acquire the lock. If the lock has already - * been acquired this will wait until the lock has been released to - * re-attempt the acquire. + * Method called by a thread to acquire the lock. If the lock has already been acquired this will + * wait until the lock has been released to re-attempt the acquire. */ @Override public synchronized void acquire() throws InterruptedException { diff --git a/mutex/src/main/java/com/iluwatar/mutex/Thief.java b/mutex/src/main/java/com/iluwatar/mutex/Thief.java index f88e46d96..29caba540 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Thief.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Thief.java @@ -27,20 +27,20 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Thief is a class which continually tries to acquire a jar and take a bean - * from it. When the jar is empty the thief stops. + * Thief is a class which continually tries to acquire a jar and take a bean from it. When the jar + * is empty the thief stops. */ public class Thief extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Thief.class); /** - * The name of the thief. + * The name of the thief. */ private final String name; - + /** - * The jar + * The jar. */ private final Jar jar; @@ -50,8 +50,7 @@ public class Thief extends Thread { } /** - * In the run method the thief repeatedly tries to take a bean until none - * are left. + * In the run method the thief repeatedly tries to take a bean until none are left. */ @Override public void run() {