}
class Part {
diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml
index cf49544cd1e0..ef190e088d5e 100644
--- a/abstract-document/pom.xml
+++ b/abstract-document/pom.xml
@@ -1,8 +1,10 @@
-
- 4.0.0
-
- java-design-patterns
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- abstract-document
-
-
- junit
- junit
- test
-
-
-
\ No newline at end of file
+
+ 4.0.0
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ abstract-document
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.abstractdocument.App
+
+
+
+
+
+
+
+
+
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java
index 36a946cc9eca..ab5ccada1414 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,51 +24,78 @@
*/
package com.iluwatar.abstractdocument;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
-/**
- * Abstract implementation of Document interface
- */
+/** Abstract implementation of Document interface. */
public abstract class AbstractDocument implements Document {
- private final Map properties;
+ private final Map documentProperties;
protected AbstractDocument(Map properties) {
Objects.requireNonNull(properties, "properties map is required");
- this.properties = properties;
+ this.documentProperties = properties;
}
@Override
public Void put(String key, Object value) {
- properties.put(key, value);
+ documentProperties.put(key, value);
return null;
}
@Override
public Object get(String key) {
- return properties.get(key);
+ return documentProperties.get(key);
}
@Override
- public Stream children(String key, Function, T> constructor) {
- Optional>> any = Stream.of(get(key)).filter(el -> el != null)
- .map(el -> (List>) el).findAny();
- return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty();
+ public Stream children(String key, Function, T> childConstructor) {
+ return Stream.ofNullable(get(key))
+ .filter(Objects::nonNull)
+ .map(el -> (List>) el)
+ .findAny()
+ .stream()
+ .flatMap(Collection::stream)
+ .map(childConstructor);
}
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
+ return buildStringRepresentation();
+ }
+
+ private String buildStringRepresentation() {
+ var builder = new StringBuilder();
builder.append(getClass().getName()).append("[");
- properties.entrySet()
- .forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]"));
+
+ // Explaining variable for document properties map
+ Map documentProperties = this.documentProperties;
+
+ // Explaining variable for the size of document properties map
+ int numProperties = documentProperties.size();
+
+ // Explaining variable for tracking the current property index
+ int currentPropertyIndex = 0;
+
+ // Iterate over document properties map
+ for (Map.Entry entry : documentProperties.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ // Append key-value pair
+ builder.append("[").append(key).append(" : ").append(value).append("]");
+
+ // Add comma if not last property
+ if (++currentPropertyIndex < numProperties) {
+ builder.append(", ");
+ }
+ }
+
builder.append("]");
return builder.toString();
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
index 83f6ab3d59dd..607b4a7f7913 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -23,69 +25,64 @@
package com.iluwatar.abstractdocument;
import com.iluwatar.abstractdocument.domain.Car;
-import com.iluwatar.abstractdocument.domain.HasModel;
-import com.iluwatar.abstractdocument.domain.HasParts;
-import com.iluwatar.abstractdocument.domain.HasPrice;
-import com.iluwatar.abstractdocument.domain.HasType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Arrays;
-import java.util.HashMap;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.List;
import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
/**
- * The Abstract Document pattern enables handling additional, non-static
- * properties. This pattern uses concept of traits to enable type safety and
- * separate properties of different classes into set of interfaces.
- *
- *
- * In Abstract Document pattern,({@link AbstractDocument}) fully implements
- * {@link Document}) interface. Traits are then defined to enable access to
- * properties in usual, static way.
+ * The Abstract Document pattern enables handling additional, non-static properties. This pattern
+ * uses concept of traits to enable type safety and separate properties of different classes into
+ * set of interfaces.
+ *
+ *
In Abstract Document pattern,({@link AbstractDocument}) fully implements {@link Document})
+ * interface. Traits are then defined to enable access to properties in usual, static way.
*/
+@Slf4j
public class App {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
/**
- * Executes the App
+ * Program entry point.
+ *
+ * @param args command line args
*/
- public App() {
+ public static void main(String[] args) {
LOGGER.info("Constructing parts and car");
- Map carProperties = new HashMap<>();
- carProperties.put(HasModel.PROPERTY, "300SL");
- carProperties.put(HasPrice.PROPERTY, 10000L);
+ var wheelProperties =
+ Map.of(
+ Property.TYPE.toString(), "wheel",
+ Property.MODEL.toString(), "15C",
+ Property.PRICE.toString(), 100L);
- Map wheelProperties = new HashMap<>();
- wheelProperties.put(HasType.PROPERTY, "wheel");
- wheelProperties.put(HasModel.PROPERTY, "15C");
- wheelProperties.put(HasPrice.PROPERTY, 100L);
+ var doorProperties =
+ Map.of(
+ Property.TYPE.toString(), "door",
+ Property.MODEL.toString(), "Lambo",
+ Property.PRICE.toString(), 300L);
- Map doorProperties = new HashMap<>();
- doorProperties.put(HasType.PROPERTY, "door");
- doorProperties.put(HasModel.PROPERTY, "Lambo");
- doorProperties.put(HasPrice.PROPERTY, 300L);
+ var carProperties =
+ Map.of(
+ Property.MODEL.toString(),
+ "300SL",
+ Property.PRICE.toString(),
+ 10000L,
+ Property.PARTS.toString(),
+ List.of(wheelProperties, doorProperties));
- carProperties.put(HasParts.PROPERTY, Arrays.asList(wheelProperties, doorProperties));
-
- Car car = new Car(carProperties);
+ var car = new Car(carProperties);
LOGGER.info("Here is our car:");
- LOGGER.info("-> model: {}", car.getModel().get());
- LOGGER.info("-> price: {}", car.getPrice().get());
+ LOGGER.info("-> model: {}", car.getModel().orElseThrow());
+ LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
LOGGER.info("-> parts: ");
- car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}", p.getType().get(), p.getModel().get(), p.getPrice().get()));
+ car.getParts()
+ .forEach(
+ p ->
+ LOGGER.info(
+ "\t{}/{}/{}",
+ p.getType().orElse(null),
+ p.getModel().orElse(null),
+ p.getPrice().orElse(null)));
}
-
- /**
- * Program entry point
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- new App();
- }
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
index 13782351492f..79a51b610337 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -26,22 +28,20 @@
import java.util.function.Function;
import java.util.stream.Stream;
-/**
- * Document interface
- */
+/** Document interface. */
public interface Document {
/**
- * Puts the value related to the key
+ * Puts the value related to the key.
*
- * @param key element key
+ * @param key element key
* @param value element value
* @return Void
*/
Void put(String key, Object value);
/**
- * Gets the value for the key
+ * Gets the value for the key.
*
* @param key element key
* @return value or null
@@ -49,9 +49,9 @@ public interface Document {
Object get(String key);
/**
- * Gets the stream of child documents
+ * Gets the stream of child documents.
*
- * @param key element key
+ * @param key element key
* @param constructor constructor of child class
* @return child documents
*/
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
index 44f640b415fd..93fbbb9c1eae 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,17 +24,13 @@
*/
package com.iluwatar.abstractdocument.domain;
-import java.util.Map;
-
import com.iluwatar.abstractdocument.AbstractDocument;
+import java.util.Map;
-/**
- * Car entity
- */
+/** Car entity. */
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
public Car(Map properties) {
super(properties);
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
index 252a4644f289..6f517588e5a0 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,19 +24,14 @@
*/
package com.iluwatar.abstractdocument.domain;
-import java.util.Optional;
-
import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.Optional;
-/**
- * HasModel trait for static access to 'model' property
- */
+/** HasModel trait for static access to 'model' property. */
public interface HasModel extends Document {
- String PROPERTY = "model";
-
default Optional getModel() {
- return Optional.ofNullable((String) get(PROPERTY));
+ return Optional.ofNullable((String) get(Property.MODEL.toString()));
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
index 9df836376f55..8bffa753e6ef 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,19 +24,14 @@
*/
package com.iluwatar.abstractdocument.domain;
-import java.util.stream.Stream;
-
import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.stream.Stream;
-/**
- * HasParts trait for static access to 'parts' property
- */
+/** HasParts trait for static access to 'parts' property. */
public interface HasParts extends Document {
- String PROPERTY = "parts";
-
default Stream getParts() {
- return children(PROPERTY, Part::new);
+ return children(Property.PARTS.toString(), Part::new);
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
index 39e4d159ce58..ce876e5faf54 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,19 +24,14 @@
*/
package com.iluwatar.abstractdocument.domain;
-import java.util.Optional;
-
import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.Optional;
-/**
- * HasPrice trait for static access to 'price' property
- */
+/** HasPrice trait for static access to 'price' property. */
public interface HasPrice extends Document {
- String PROPERTY = "price";
-
default Optional getPrice() {
- return Optional.ofNullable((Number) get(PROPERTY));
+ return Optional.ofNullable((Number) get(Property.PRICE.toString()));
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
index 4a80bff20c16..5e0f49df7b7b 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -23,18 +25,13 @@
package com.iluwatar.abstractdocument.domain;
import com.iluwatar.abstractdocument.Document;
-
+import com.iluwatar.abstractdocument.domain.enums.Property;
import java.util.Optional;
-/**
- * HasType trait for static access to 'type' property
- */
+/** HasType trait for static access to 'type' property. */
public interface HasType extends Document {
- String PROPERTY = "type";
-
default Optional getType() {
- return Optional.ofNullable((String) get(PROPERTY));
+ return Optional.ofNullable((String) get(Property.TYPE.toString()));
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
index 649ccf41365b..6eec08b0d2a4 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,17 +24,13 @@
*/
package com.iluwatar.abstractdocument.domain;
-import java.util.Map;
-
import com.iluwatar.abstractdocument.AbstractDocument;
+import java.util.Map;
-/**
- * Part entity
- */
+/** Part entity. */
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
public Part(Map properties) {
super(properties);
}
-
}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java
new file mode 100644
index 000000000000..3e0d6d10ab1f
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java
@@ -0,0 +1,33 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.abstractdocument.domain.enums;
+
+/** Enum To Describe Property type. */
+public enum Property {
+ PARTS,
+ TYPE,
+ PRICE,
+ MODEL
+}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
index f6686ff8fd19..a098517c3a69 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,67 +24,107 @@
*/
package com.iluwatar.abstractdocument;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.*;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNotNull;
-
-/**
- * AbstractDocument test class
- */
-public class AbstractDocumentTest {
+/** AbstractDocument test class */
+class AbstractDocumentTest {
private static final String KEY = "key";
private static final String VALUE = "value";
- private class DocumentImplementation extends AbstractDocument {
+ private static class DocumentImplementation extends AbstractDocument {
DocumentImplementation(Map properties) {
super(properties);
}
}
- private DocumentImplementation document = new DocumentImplementation(new HashMap<>());
+ private final DocumentImplementation document = new DocumentImplementation(new HashMap<>());
@Test
- public void shouldPutAndGetValue() {
+ void shouldPutAndGetValue() {
document.put(KEY, VALUE);
assertEquals(VALUE, document.get(KEY));
}
@Test
- public void shouldRetrieveChildren() {
- Map child1 = new HashMap<>();
- Map child2 = new HashMap<>();
- List> children = Arrays.asList(child1, child2);
+ void shouldRetrieveChildren() {
+ var children = List.of(Map.of(), Map.of());
document.put(KEY, children);
- Stream childrenStream = document.children(KEY, DocumentImplementation::new);
+ var childrenStream = document.children(KEY, DocumentImplementation::new);
assertNotNull(children);
assertEquals(2, childrenStream.count());
}
@Test
- public void shouldRetrieveEmptyStreamForNonExistingChildren() {
- Stream children = document.children(KEY, DocumentImplementation::new);
+ void shouldRetrieveEmptyStreamForNonExistingChildren() {
+ var children = document.children(KEY, DocumentImplementation::new);
assertNotNull(children);
assertEquals(0, children.count());
}
@Test
- public void shouldIncludePropsInToString() {
- Map props = new HashMap<>();
- props.put(KEY, VALUE);
- DocumentImplementation document = new DocumentImplementation(props);
- assertNotNull(document.toString().contains(KEY));
- assertNotNull(document.toString().contains(VALUE));
+ void shouldIncludePropsInToString() {
+ var props = Map.of(KEY, (Object) VALUE);
+ var document = new DocumentImplementation(props);
+ assertTrue(document.toString().contains(KEY));
+ assertTrue(document.toString().contains(VALUE));
+ }
+
+ @Test
+ void shouldHandleExceptionDuringConstruction() {
+ Map invalidProperties =
+ null; // Invalid properties, causing NullPointerException
+
+ // Throw null pointer exception
+ assertThrows(
+ NullPointerException.class,
+ () -> {
+ // Attempt to construct a document with invalid properties
+ new DocumentImplementation(invalidProperties);
+ });
+ }
+
+ @Test
+ void shouldPutAndGetNestedDocument() {
+ // Creating a nested document
+ DocumentImplementation nestedDocument = new DocumentImplementation(new HashMap<>());
+ nestedDocument.put("nestedKey", "nestedValue");
+
+ document.put("nested", nestedDocument);
+
+ // Retrieving the nested document
+ DocumentImplementation retrievedNestedDocument =
+ (DocumentImplementation) document.get("nested");
+
+ assertNotNull(retrievedNestedDocument);
+ assertEquals("nestedValue", retrievedNestedDocument.get("nestedKey"));
}
+ @Test
+ void shouldUpdateExistingValue() {
+ // Arrange
+ final String key = "key";
+ final String originalValue = "originalValue";
+ final String updatedValue = "updatedValue";
+
+ // Initializing the value
+ document.put(key, originalValue);
+
+ // Verifying that the initial value is retrieved correctly
+ assertEquals(originalValue, document.get(key));
+
+ // Updating the value
+ document.put(key, updatedValue);
+
+ // Verifying that the updated value is retrieved correctly
+ assertEquals(updatedValue, document.get(key));
+ }
}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
index b91f57b2746c..16dcba0db37f 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,16 +24,21 @@
*/
package com.iluwatar.abstractdocument;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Simple App test
- */
-public class AppTest {
+import org.junit.jupiter.api.Test;
+
+/** Simple App test */
+class AppTest {
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
+ */
@Test
- public void shouldExecuteAppWithoutException() {
- App.main(null);
+ void shouldExecuteAppWithoutException() {
+ assertDoesNotThrow(() -> App.main(null));
}
-
}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
index 03d75d5e5e6e..fc29dea45c43 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,24 +24,17 @@
*/
package com.iluwatar.abstractdocument;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import com.iluwatar.abstractdocument.domain.Car;
-import com.iluwatar.abstractdocument.domain.HasModel;
-import com.iluwatar.abstractdocument.domain.HasParts;
-import com.iluwatar.abstractdocument.domain.HasPrice;
-import com.iluwatar.abstractdocument.domain.HasType;
import com.iluwatar.abstractdocument.domain.Part;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.HashMap;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.List;
import java.util.Map;
+import org.junit.jupiter.api.Test;
-import static junit.framework.TestCase.assertEquals;
-
-/**
- * Test for Part and Car
- */
-public class DomainTest {
+/** Test for Part and Car */
+class DomainTest {
private static final String TEST_PART_TYPE = "test-part-type";
private static final String TEST_PART_MODEL = "test-part-model";
@@ -49,29 +44,28 @@ public class DomainTest {
private static final long TEST_CAR_PRICE = 1L;
@Test
- public void shouldConstructPart() {
- Map partProperties = new HashMap<>();
- partProperties.put(HasType.PROPERTY, TEST_PART_TYPE);
- partProperties.put(HasModel.PROPERTY, TEST_PART_MODEL);
- partProperties.put(HasPrice.PROPERTY, TEST_PART_PRICE);
- Part part = new Part(partProperties);
-
- assertEquals(TEST_PART_TYPE, part.getType().get());
- assertEquals(TEST_PART_MODEL, part.getModel().get());
- assertEquals(TEST_PART_PRICE, part.getPrice().get());
+ void shouldConstructPart() {
+ var partProperties =
+ Map.of(
+ Property.TYPE.toString(), TEST_PART_TYPE,
+ Property.MODEL.toString(), TEST_PART_MODEL,
+ Property.PRICE.toString(), (Object) TEST_PART_PRICE);
+ var part = new Part(partProperties);
+ assertEquals(TEST_PART_TYPE, part.getType().orElseThrow());
+ assertEquals(TEST_PART_MODEL, part.getModel().orElseThrow());
+ assertEquals(TEST_PART_PRICE, part.getPrice().orElseThrow());
}
@Test
- public void shouldConstructCar() {
- Map carProperties = new HashMap<>();
- carProperties.put(HasModel.PROPERTY, TEST_CAR_MODEL);
- carProperties.put(HasPrice.PROPERTY, TEST_CAR_PRICE);
- carProperties.put(HasParts.PROPERTY, Arrays.asList(new HashMap<>(), new HashMap<>()));
- Car car = new Car(carProperties);
-
- assertEquals(TEST_CAR_MODEL, car.getModel().get());
- assertEquals(TEST_CAR_PRICE, car.getPrice().get());
+ void shouldConstructCar() {
+ var carProperties =
+ Map.of(
+ Property.MODEL.toString(), TEST_CAR_MODEL,
+ Property.PRICE.toString(), TEST_CAR_PRICE,
+ Property.PARTS.toString(), List.of(Map.of(), Map.of()));
+ var car = new Car(carProperties);
+ assertEquals(TEST_CAR_MODEL, car.getModel().orElseThrow());
+ assertEquals(TEST_CAR_PRICE, car.getPrice().orElseThrow());
assertEquals(2, car.getParts().count());
}
-
}
diff --git a/abstract-factory/README.md b/abstract-factory/README.md
index c049401fcd5c..ee9823a39a5f 100644
--- a/abstract-factory/README.md
+++ b/abstract-factory/README.md
@@ -1,27 +1,32 @@
---
-layout: pattern
-title: Abstract Factory
-folder: abstract-factory
-permalink: /patterns/abstract-factory/
-pumlid: PSZB3OD034NHLa81Czwd6sCC39gVxEUWT1_ssLmTtQLqgR5fM7sTmFGtaV6TZu8prd0r6HtQaMKqAZLk1XjT_E6qgPUZfyc0MdTgx0-8LuUn8ErFXdr98NypXxKyezKV
-categories: Creational
-tags:
- - Java
- - Gang Of Four
- - Difficulty-Intermediate
+title: "Abstract Factory Pattern in Java: Mastering Object Creation Elegantly"
+shortTitle: Abstract Factory
+description: "Learn the Abstract Factory pattern in Java with real-world examples, class diagrams, and tutorials. Understand its intent, applicability, benefits, and known uses to enhance your design pattern knowledge."
+category: Creational
+language: en
+tag:
+ - Abstraction
+ - Decoupling
+ - Gang of Four
+ - Instantiation
+ - Polymorphism
---
## Also known as
-Kit
-## Intent
-Provide an interface for creating families of related or dependent
-objects without specifying their concrete classes.
+* Kit
-## Explanation
-Real world example
+## Intent of Abstract Factory Design Pattern
-> To create a kingdom we need objects with common theme. Elven kingdom needs an Elven king, Elven castle and Elven army whereas Orcish kingdom needs an Orcish king, Orcish castle and Orcish army. There is a dependency between the objects in the kingdom.
+The Abstract Factory pattern in Java provides an interface for creating families of related or dependent objects without specifying their concrete classes, enhancing modularity and flexibility in software design.
+
+## Detailed Explanation of Abstract Factory Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a furniture company that uses the Abstract Factory pattern in Java to produce various styles of furniture: modern, Victorian, and rustic. Each style includes products like chairs, tables, and sofas. To ensure consistency within each style, the company uses an Abstract Factory pattern.
+>
+> In this scenario, the Abstract Factory is an interface for creating families of related furniture objects (chairs, tables, sofas). Each concrete factory (ModernFurnitureFactory, VictorianFurnitureFactory, RusticFurnitureFactory) implements the Abstract Factory interface and creates a set of products that match the specific style. This way, clients can create a whole set of modern or Victorian furniture without worrying about the details of their instantiation. This maintains a consistent style and allows easy swapping of one style of furniture for another.
In plain words
@@ -31,122 +36,192 @@ Wikipedia says
> The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
-**Programmatic Example**
+Class diagram
-Translating the kingdom example above. First of all we have some interfaces and implementation for the objects in the kingdom
+
-```
+## Programmatic Example of Abstract Factory in Java
+
+To create a kingdom using the Abstract Factory pattern in Java, we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
+
+Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom.
+
+```java
public interface Castle {
- String getDescription();
+ String getDescription();
}
+
public interface King {
- String getDescription();
+ String getDescription();
}
+
public interface Army {
- String getDescription();
+ String getDescription();
}
// Elven implementations ->
public class ElfCastle implements Castle {
- static final String DESCRIPTION = "This is the Elven castle!";
- @Override
- public String getDescription() {
- return DESCRIPTION;
- }
+ static final String DESCRIPTION = "This is the elven castle!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
}
+
public class ElfKing implements King {
- static final String DESCRIPTION = "This is the Elven king!";
- @Override
- public String getDescription() {
- return DESCRIPTION;
- }
+ static final String DESCRIPTION = "This is the elven king!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
}
+
public class ElfArmy implements Army {
- static final String DESCRIPTION = "This is the Elven Army!";
- @Override
- public String getDescription() {
- return DESCRIPTION;
- }
-}
+ static final String DESCRIPTION = "This is the elven Army!";
-// Orcish implementations similarly...
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+// Orcish implementations similarly -> ...
```
-Then we have the abstraction and implementations for the kingdom factory
+Then we have the abstraction and implementations for the kingdom factory.
-```
+```java
public interface KingdomFactory {
- Castle createCastle();
- King createKing();
- Army createArmy();
+ Castle createCastle();
+
+ King createKing();
+
+ Army createArmy();
}
public class ElfKingdomFactory implements KingdomFactory {
- public Castle createCastle() {
- return new ElfCastle();
- }
- public King createKing() {
- return new ElfKing();
- }
- public Army createArmy() {
- return new ElfArmy();
- }
+
+ @Override
+ public Castle createCastle() {
+ return new ElfCastle();
+ }
+
+ @Override
+ public King createKing() {
+ return new ElfKing();
+ }
+
+ @Override
+ public Army createArmy() {
+ return new ElfArmy();
+ }
}
-public class OrcKingdomFactory implements KingdomFactory {
- public Castle createCastle() {
- return new OrcCastle();
- }
- public King createKing() {
- return new OrcKing();
- }
- public Army createArmy() {
- return new OrcArmy();
- }
+// Orcish implementations similarly -> ...
+```
+
+Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`, responsible for returning an instance of either `ElfKingdomFactory` or `OrcKingdomFactory`. The client can use `FactoryMaker` to create the desired concrete factory which, in turn, will produce different concrete objects (derived from `Army`, `King`, `Castle`). In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
+
+```java
+public static class FactoryMaker {
+
+ public enum KingdomType {
+ ELF, ORC
+ }
+
+ public static KingdomFactory makeFactory(KingdomType type) {
+ return switch (type) {
+ case ELF -> new ElfKingdomFactory();
+ case ORC -> new OrcKingdomFactory();
+ };
+ }
}
```
-Now we have our abstract factory that lets us make family of related objects i.e. Elven kingdom factory creates Elven castle, king and army etc.
+Here is the main function of our example application:
+
+```java
+LOGGER.info("elf kingdom");
+createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+LOGGER.info(kingdom.getArmy().getDescription());
+LOGGER.info(kingdom.getCastle().getDescription());
+LOGGER.info(kingdom.getKing().getDescription());
+
+LOGGER.info("orc kingdom");
+createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+LOGGER.info(kingdom.getArmy().getDescription());
+LOGGER.info(kingdom.getCastle().getDescription());
+LOGGER.info(kingdom.getKing().getDescription());
+```
+
+The program output:
```
-KingdomFactory factory = new ElfKingdomFactory();
-Castle castle = factory.createCastle();
-King king = factory.createKing();
-Army army = factory.createArmy();
-
-castle.getDescription(); // Output: This is the Elven castle!
-king.getDescription(); // Output: This is the Elven king!
-army.getDescription(); // Output: This is the Elven Army!
+07:35:46.340 [main] INFO com.iluwatar.abstractfactory.App -- elf kingdom
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven army!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven castle!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven king!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- orc kingdom
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc army!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc castle!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king!
```
-## Applicability
-Use the Abstract Factory pattern when
+## When to Use the Abstract Factory Pattern in Java
-* a system should be independent of how its products are created, composed and represented
-* a system should be configured with one of multiple families of products
-* a family of related product objects is designed to be used together, and you need to enforce this constraint
-* you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
-* the lifetime of the dependency is conceptually shorter than the lifetime of the consumer.
-* you need a run-time value to construct a particular dependency
-* you want to decide which product to call from a family at runtime.
-* you need to supply one or more parameters only known at run-time before you can resolve a dependency.
+Use the Abstract Factory pattern in Java when:
-## Use Cases:
+* The system should be independent of how its products are created, composed, and represented.
+* You need to configure the system with one of multiple families of products.
+* A family of related product objects must be used together, enforcing consistency.
+* You want to provide a class library of products, exposing only their interfaces, not their implementations.
+* The lifetime of dependencies is shorter than the consumer's lifetime.
+* Dependencies need to be constructed using runtime values or parameters.
+* You need to choose which product to use from a family at runtime.
+* Adding new products or families should not require changes to existing code.
-* Selecting to call the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService at runtime.
-* Unit test case writing becomes much easier
+## Abstract Factory Pattern Java Tutorials
-## Consequences:
+* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java)
+* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory)
-* Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.
+## Benefits and Trade-offs of Abstract Factory Pattern
-## Real world examples
+Benefits:
+* Flexibility: Easily switch between product families without code modifications.
+
+* Decoupling: Client code only interacts with abstract interfaces, promoting portability and maintainability.
+
+* Reusability: Abstract factories and products facilitate component reuse across projects.
+
+* Maintainability: Changes to individual product families are localized, simplifying updates.
+
+Trade-offs:
+
+* Complexity: Defining abstract interfaces and concrete factories adds initial overhead.
+
+* Indirectness: Client code interacts with products indirectly through factories, potentially reducing transparency.
+
+## Real-World Applications of Abstract Factory Pattern in Java
+
+* Java Swing's `LookAndFeel` classes for providing different look-and-feel options.
+* Various implementations in the Java Abstract Window Toolkit (AWT) for creating different GUI components.
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
-## Credits
+## Related Java Design Patterns
+
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Abstract Factory uses Factory Methods to create products.
+* [Singleton](https://java-design-patterns.com/patterns/singleton/): Abstract Factory classes are often implemented as Singletons.
+* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): Similar to Abstract Factory but focuses on configuring and managing a set of related objects in a flexible way.
+
+## References and Credits
-* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Design Patterns in Java](https://amzn.to/3Syw0vC)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3HWNf4U)
diff --git a/abstract-factory/etc/abstract-factory.urm.png b/abstract-factory/etc/abstract-factory.urm.png
new file mode 100644
index 000000000000..836858a2c652
Binary files /dev/null and b/abstract-factory/etc/abstract-factory.urm.png differ
diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml
new file mode 100644
index 000000000000..999091ef54f6
--- /dev/null
+++ b/abstract-factory/etc/abstract-factory.urm.puml
@@ -0,0 +1,101 @@
+@startuml
+package com.iluwatar.abstractfactory {
+ class App {
+ - LOGGER : Logger {static}
+ - army : Army
+ - castle : Castle
+ - king : King
+ + App()
+ + createKingdom(factory : KingdomFactory)
+ + getArmy() : Army
+ ~ getArmy(factory : KingdomFactory) : Army
+ + getCastle() : Castle
+ ~ getCastle(factory : KingdomFactory) : Castle
+ + getKing() : King
+ ~ getKing(factory : KingdomFactory) : King
+ + main(args : String[]) {static}
+ - setArmy(army : Army)
+ - setCastle(castle : Castle)
+ - setKing(king : King)
+ }
+ class FactoryMaker {
+ + FactoryMaker()
+ + makeFactory(type : KingdomType) : KingdomFactory {static}
+ }
+ enum KingdomType {
+ + ELF {static}
+ + ORC {static}
+ + valueOf(name : String) : KingdomType {static}
+ + values() : KingdomType[] {static}
+ }
+ interface Army {
+ + getDescription() : String {abstract}
+ }
+ interface Castle {
+ + getDescription() : String {abstract}
+ }
+ class ElfArmy {
+ ~ DESCRIPTION : String {static}
+ + ElfArmy()
+ + getDescription() : String
+ }
+ class ElfCastle {
+ ~ DESCRIPTION : String {static}
+ + ElfCastle()
+ + getDescription() : String
+ }
+ class ElfKing {
+ ~ DESCRIPTION : String {static}
+ + ElfKing()
+ + getDescription() : String
+ }
+ class ElfKingdomFactory {
+ + ElfKingdomFactory()
+ + createArmy() : Army
+ + createCastle() : Castle
+ + createKing() : King
+ }
+ interface King {
+ + getDescription() : String {abstract}
+ }
+ interface KingdomFactory {
+ + createArmy() : Army {abstract}
+ + createCastle() : Castle {abstract}
+ + createKing() : King {abstract}
+ }
+ class OrcArmy {
+ ~ DESCRIPTION : String {static}
+ + OrcArmy()
+ + getDescription() : String
+ }
+ class OrcCastle {
+ ~ DESCRIPTION : String {static}
+ + OrcCastle()
+ + getDescription() : String
+ }
+ class OrcKing {
+ ~ DESCRIPTION : String {static}
+ + OrcKing()
+ + getDescription() : String
+ }
+ class OrcKingdomFactory {
+ + OrcKingdomFactory()
+ + createArmy() : Army
+ + createCastle() : Castle
+ + createKing() : King
+ }
+}
+KingdomType ..+ FactoryMaker
+App --> "-castle" Castle
+FactoryMaker ..+ App
+App --> "-king" King
+App --> "-army" Army
+ElfArmy ..|> Army
+ElfCastle ..|> Castle
+ElfKing ..|> King
+ElfKingdomFactory ..|> KingdomFactory
+OrcArmy ..|> Army
+OrcCastle ..|> Castle
+OrcKing ..|> King
+OrcKingdomFactory ..|> KingdomFactory
+@enduml
\ No newline at end of file
diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml
index f3a266f8ef7d..60fbf72eff54 100644
--- a/abstract-factory/pom.xml
+++ b/abstract-factory/pom.xml
@@ -1,8 +1,10 @@
-
+
-
+
4.0.0
com.iluwatar
java-design-patterns
- 1.18.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
abstract-factory
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.abstractfactory.App
+
+
+
+
+
+
+
+
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
index d7cb296c0b18..798cbe4fd118 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,99 +24,63 @@
*/
package com.iluwatar.abstractfactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
/**
- *
- * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme
- * without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of
- * the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part
- * of the theme. The client does not know (or care) which concrete objects it gets from each of these internal
- * factories, since it uses only the generic interfaces of their products. This pattern separates the details of
- * implementation of a set of objects from their general usage and relies on object composition, as object creation is
- * implemented in methods exposed in the factory interface.
- *
- * The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and its implementations (
- * {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both concrete implementations to create a
- * king, a castle and an army.
- *
+ * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
+ * have a common theme without specifying their concrete classes. In normal usage, the client
+ * software creates a concrete implementation of the abstract factory and then uses the generic
+ * interface of the factory to create the concrete objects that are part of the theme. The client
+ * does not know (or care) which concrete objects it gets from each of these internal factories,
+ * since it uses only the generic interfaces of their products. This pattern separates the details
+ * of implementation of a set of objects from their general usage and relies on object composition,
+ * as object creation is implemented in methods exposed in the factory interface.
+ *
+ *
The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory})
+ * and its implementations ( {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses
+ * both concrete implementations to create a king, a castle, and an army.
*/
-public class App {
+@Slf4j
+@Getter
+public class App implements Runnable {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
- private King king;
- private Castle castle;
- private Army army;
+ private final Kingdom kingdom = new Kingdom();
/**
- * Creates kingdom
+ * Program entry point.
+ *
+ * @param args command line args
*/
- public void createKingdom(final KingdomFactory factory) {
- setKing(factory.createKing());
- setCastle(factory.createCastle());
- setArmy(factory.createArmy());
- }
-
- King getKing(final KingdomFactory factory) {
- return factory.createKing();
- }
-
- public King getKing() {
- return king;
- }
-
- private void setKing(final King king) {
- this.king = king;
- }
-
- Castle getCastle(final KingdomFactory factory) {
- return factory.createCastle();
- }
-
- public Castle getCastle() {
- return castle;
- }
-
- private void setCastle(final Castle castle) {
- this.castle = castle;
- }
-
- Army getArmy(final KingdomFactory factory) {
- return factory.createArmy();
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
}
- public Army getArmy() {
- return army;
+ @Override
+ public void run() {
+ LOGGER.info("elf kingdom");
+ createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ LOGGER.info(kingdom.getArmy().getDescription());
+ LOGGER.info(kingdom.getCastle().getDescription());
+ LOGGER.info(kingdom.getKing().getDescription());
+
+ LOGGER.info("orc kingdom");
+ createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ LOGGER.info(kingdom.getArmy().getDescription());
+ LOGGER.info(kingdom.getCastle().getDescription());
+ LOGGER.info(kingdom.getKing().getDescription());
}
- private void setArmy(final Army army) {
- this.army = army;
- }
-
/**
- * Program entry point
- *
- * @param args
- * command line args
+ * Creates kingdom.
+ *
+ * @param kingdomType type of Kingdom
*/
- public static void main(String[] args) {
-
- App app = new App();
-
- LOGGER.info("Elf Kingdom");
- app.createKingdom(new ElfKingdomFactory());
- LOGGER.info(app.getArmy().getDescription());
- LOGGER.info(app.getCastle().getDescription());
- LOGGER.info(app.getKing().getDescription());
-
- LOGGER.info("Orc Kingdom");
- app.createKingdom(new OrcKingdomFactory());
- LOGGER.info(app.getArmy().getDescription());
- LOGGER.info(app.getCastle().getDescription());
- LOGGER.info(app.getKing().getDescription());
-
+ public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
+ final KingdomFactory kingdomFactory = Kingdom.FactoryMaker.makeFactory(kingdomType);
+ kingdom.setKing(kingdomFactory.createKing());
+ kingdom.setCastle(kingdomFactory.createCastle());
+ kingdom.setArmy(kingdomFactory.createArmy());
}
-
}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
index 3d107dd3479e..78c75323f1c0 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,11 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * Army interface
- *
- */
+/** Army interface. */
public interface Army {
String getDescription();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
index 837952d0d364..ee1e16f3cd3f 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,11 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * Castle interface
- *
- */
+/** Castle interface. */
public interface Castle {
String getDescription();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
index f9687073df29..d7e46c1456f0 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,14 +24,10 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * ElfArmy
- *
- */
+/** ElfArmy. */
public class ElfArmy implements Army {
- static final String DESCRIPTION = "This is the Elven Army!";
+ static final String DESCRIPTION = "This is the elven army!";
@Override
public String getDescription() {
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
index 108bdc8d315a..136afb11fd23 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,14 +24,10 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * ElfCastle
- *
- */
+/** ElfCastle. */
public class ElfCastle implements Castle {
- static final String DESCRIPTION = "This is the Elven castle!";
+ static final String DESCRIPTION = "This is the elven castle!";
@Override
public String getDescription() {
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
index f6e400bc0d8c..9b0d3a6f1a77 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,14 +24,10 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * ElfKing
- *
- */
+/** ElfKing. */
public class ElfKing implements King {
- static final String DESCRIPTION = "This is the Elven king!";
+ static final String DESCRIPTION = "This is the elven king!";
@Override
public String getDescription() {
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
index 31cfddb70ff8..b09a2f47c252 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,23 +24,21 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * ElfKingdomFactory concrete factory.
- *
- */
+/** ElfKingdomFactory concrete factory. */
public class ElfKingdomFactory implements KingdomFactory {
+ @Override
public Castle createCastle() {
return new ElfCastle();
}
+ @Override
public King createKing() {
return new ElfKing();
}
+ @Override
public Army createArmy() {
return new ElfArmy();
}
-
}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
index bffc994d24cb..9f65ed434577 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,11 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * King interface
- *
- */
+/** King interface. */
public interface King {
String getDescription();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
new file mode 100644
index 000000000000..d1f85a6a4812
--- /dev/null
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
@@ -0,0 +1,56 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.abstractfactory;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/** Helper class to manufacture {@link KingdomFactory} beans. */
+@Getter
+@Setter
+public class Kingdom {
+
+ private King king;
+ private Castle castle;
+ private Army army;
+
+ /** The factory of kingdom factories. */
+ public static class FactoryMaker {
+
+ /** Enumeration for the different types of Kingdoms. */
+ public enum KingdomType {
+ ELF,
+ ORC
+ }
+
+ /** The factory method to create KingdomFactory concrete objects. */
+ public static KingdomFactory makeFactory(KingdomType type) {
+ return switch (type) {
+ case ELF -> new ElfKingdomFactory();
+ case ORC -> new OrcKingdomFactory();
+ };
+ }
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
index fb3434fae136..199c6697dcaa 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,11 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * KingdomFactory factory interface.
- *
- */
+/** KingdomFactory factory interface. */
public interface KingdomFactory {
Castle createCastle();
@@ -34,5 +32,4 @@ public interface KingdomFactory {
King createKing();
Army createArmy();
-
}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
index 31552f4301db..31ed6896d921 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,14 +24,10 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * OrcArmy
- *
- */
+/** OrcArmy. */
public class OrcArmy implements Army {
- static final String DESCRIPTION = "This is the Orc Army!";
+ static final String DESCRIPTION = "This is the orc army!";
@Override
public String getDescription() {
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
index e72da4a77b50..bdae5709a97c 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,14 +24,10 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * OrcCastle
- *
- */
+/** OrcCastle. */
public class OrcCastle implements Castle {
- static final String DESCRIPTION = "This is the Orc castle!";
+ static final String DESCRIPTION = "This is the orc castle!";
@Override
public String getDescription() {
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
index f48115f89d3a..7f106d45a01d 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,14 +24,10 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * OrcKing
- *
- */
+/** OrcKing. */
public class OrcKing implements King {
- static final String DESCRIPTION = "This is the Orc king!";
+ static final String DESCRIPTION = "This is the orc king!";
@Override
public String getDescription() {
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
index 5b2ba3a30fc9..82d258570460 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,21 +24,20 @@
*/
package com.iluwatar.abstractfactory;
-/**
- *
- * OrcKingdomFactory concrete factory.
- *
- */
+/** OrcKingdomFactory concrete factory. */
public class OrcKingdomFactory implements KingdomFactory {
+ @Override
public Castle createCastle() {
return new OrcCastle();
}
+ @Override
public King createKing() {
return new OrcKing();
}
+ @Override
public Army createArmy() {
return new OrcArmy();
}
diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
index b6fca8b2389f..b5dde940c464 100644
--- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,63 +24,69 @@
*/
package com.iluwatar.abstractfactory;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Before;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Test for abstract factory
- */
-public class AbstractFactoryTest {
+import org.junit.jupiter.api.Test;
- private App app = new App();
- private KingdomFactory elfFactory;
- private KingdomFactory orcFactory;
+/** Tests for abstract factory. */
+class AbstractFactoryTest {
- @Before
- public void setUp() {
- elfFactory = new ElfKingdomFactory();
- orcFactory = new OrcKingdomFactory();
- }
+ private final App app = new App();
@Test
- public void king() {
- final King elfKing = app.getKing(elfFactory);
+ void verifyKingCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var elfKing = kingdom.getKing();
assertTrue(elfKing instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription());
- final King orcKing = app.getKing(orcFactory);
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcKing = kingdom.getKing();
assertTrue(orcKing instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription());
}
@Test
- public void castle() {
- final Castle elfCastle = app.getCastle(elfFactory);
+ void verifyCastleCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var elfCastle = kingdom.getCastle();
assertTrue(elfCastle instanceof ElfCastle);
assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription());
- final Castle orcCastle = app.getCastle(orcFactory);
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcCastle = kingdom.getCastle();
assertTrue(orcCastle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription());
}
@Test
- public void army() {
- final Army elfArmy = app.getArmy(elfFactory);
+ void verifyArmyCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var elfArmy = kingdom.getArmy();
assertTrue(elfArmy instanceof ElfArmy);
assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription());
- final Army orcArmy = app.getArmy(orcFactory);
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcArmy = kingdom.getArmy();
assertTrue(orcArmy instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription());
}
@Test
- public void createElfKingdom() {
- app.createKingdom(elfFactory);
- final King king = app.getKing();
- final Castle castle = app.getCastle();
- final Army army = app.getArmy();
+ void verifyElfKingdomCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var king = kingdom.getKing();
+ final var castle = kingdom.getCastle();
+ final var army = kingdom.getArmy();
assertTrue(king instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof ElfCastle);
@@ -88,11 +96,13 @@ public void createElfKingdom() {
}
@Test
- public void createOrcKingdom() {
- app.createKingdom(orcFactory);
- final King king = app.getKing();
- final Castle castle = app.getCastle();
- final Army army = app.getArmy();
+ void verifyOrcKingdomCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var kingdom = app.getKingdom();
+
+ final var king = kingdom.getKing();
+ final var castle = kingdom.getCastle();
+ final var army = kingdom.getArmy();
assertTrue(king instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof OrcCastle);
diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
index b9d5f7d906e0..9f53691a594d 100644
--- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,17 +24,16 @@
*/
package com.iluwatar.abstractfactory;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import java.io.IOException;
+import org.junit.jupiter.api.Test;
+
+/** Check whether the execution of the main method in {@link App} throws an exception. */
+class AppTest {
-/**
- * Tests that Abstract Factory example runs without errors.
- */
-public class AppTest {
@Test
- public void test() throws IOException {
- String[] args = {};
- App.main(args);
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/active-object/README.md b/active-object/README.md
new file mode 100644
index 000000000000..a804ce9e632b
--- /dev/null
+++ b/active-object/README.md
@@ -0,0 +1,222 @@
+---
+title: "Active Object Pattern in Java: Achieving Efficient Asynchronous Processing"
+shortTitle: Active Object
+description: "Learn about the Active Object design pattern in Java. This guide covers asynchronous behavior, concurrency, and practical examples to enhance your Java applications' performance."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Decoupling
+ - Messaging
+ - Synchronization
+ - Thread management
+---
+
+## Intent of Active Object Design Pattern
+
+The Active Object pattern provides a reliable method for asynchronous processing in Java, ensuring responsive applications and efficient thread management. It achieves this by encapsulating tasks within objects that have their own thread and message queue. This separation keeps the main thread responsive and avoids issues like direct thread manipulation or shared state access.
+
+## Detailed Explanation of Active Object Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy restaurant where customers place orders with waiters. Instead of the waiters going to the kitchen to prepare the food themselves, they write the orders on slips and hand them to a dispatcher. The dispatcher manages a pool of chefs who prepare the meals asynchronously. Once a chef is free, they pick up the next order from the queue, prepare the dish, and notify the waiter when it's ready for serving.
+>
+> In this analogy, the waiters represent the client threads, the dispatcher represents the scheduler, and the chefs represent the method execution in separate threads. This setup allows the waiters to continue taking orders without being blocked by the food preparation process, much like the Active Object pattern decouples method invocation from execution to enhance concurrency.
+
+In plain words
+
+> The Active Object pattern decouples method execution from method invocation to improve concurrency and responsiveness in multithreaded applications.
+
+Wikipedia says
+
+> The active object design pattern decouples method execution from method invocation for objects that each reside in their own thread of control.[1] The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.
+>
+> The pattern consists of six elements:
+>
+> * A proxy, which provides an interface towards clients with publicly accessible methods.
+> * An interface which defines the method request on an active object.
+> * A list of pending requests from clients.
+> * A scheduler, which decides which request to execute next.
+> * The implementation of the active object method.
+> * A callback or variable for the client to receive the result.
+
+Sequence diagram
+
+
+
+
+## Programmatic Example of Active Object in Java
+
+This section explains how the Active Object design pattern works in Java, highlighting its use in asynchronous task management and concurrency control.
+
+The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern.
+
+```java
+public abstract class ActiveCreature {
+ private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
+
+ private BlockingQueue requests;
+
+ private String name;
+
+ private Thread thread;
+
+ public ActiveCreature(String name) {
+ this.name = name;
+ this.requests = new LinkedBlockingQueue();
+ thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ requests.take().run();
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ }
+ }
+ }
+ }
+ );
+ thread.start();
+ }
+
+ public void eat() throws InterruptedException {
+ requests.put(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ }
+ }
+ );
+ }
+
+ public void roam() throws InterruptedException {
+ requests.put(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("{} has started to roam the wastelands.", name());
+ }
+ }
+ );
+ }
+
+ public String name() {
+ return this.name;
+ }
+}
+```
+
+We can see that any class that will extend the `ActiveCreature` class will have its own thread of control to invoke and execute methods.
+
+For example, the `Orc` class:
+
+```java
+public class Orc extends ActiveCreature {
+
+ public Orc(String name) {
+ super(name);
+ }
+}
+```
+
+Now, we can create multiple creatures such as orcs, tell them to eat and roam, and they will execute it on their own thread of control:
+
+```java
+public class App implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
+
+ private static final int NUM_CREATURES = 3;
+
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
+ }
+
+ @Override
+ public void run() {
+ List creatures = new ArrayList<>();
+ try {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.add(new Orc(Orc.class.getSimpleName() + i));
+ creatures.get(i).eat();
+ creatures.get(i).roam();
+ }
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ Thread.currentThread().interrupt();
+ } finally {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.get(i).kill(0);
+ }
+ }
+ }
+}
+```
+
+Program output:
+
+```
+09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating!
+09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating!
+09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating!
+09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating!
+09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating!
+09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands.
+09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating!
+09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands.
+09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands.
+```
+
+## When to Use the Active Object Pattern in Java
+
+Use the Active Object pattern in Java when:
+
+* when you need to handle asynchronous tasks without blocking the main thread, ensuring better performance and responsiveness.
+* When you need to interact with external resources asynchronously.
+* When you want to improve the responsiveness of your application.
+* When you need to manage concurrent tasks in a modular and maintainable way.
+
+## Active Object Pattern Java Tutorials
+
+* [Android and Java Concurrency: The Active Object Pattern(Douglas Schmidt)](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
+
+## Real-World Applications of Active Object Pattern in Java
+
+* Real-time trading systems where transaction requests are handled asynchronously.
+* GUIs where long-running tasks are executed in the background without freezing the user interface.
+* Game programming to handle concurrent updates to game state or AI computations.
+
+## Benefits and Trade-offs of Active Object Pattern
+
+Discover the benefits and trade-offs of using the Active Object pattern in Java, including improved thread safety and potential overhead concerns.
+
+Benefits:
+
+* Improves responsiveness of the main thread.
+* Encapsulates concurrency concerns within objects.
+* Promotes better code organization and maintainability.
+* Provides thread safety and avoids shared state access problems.
+
+Trade-offs:
+
+* Introduces additional overhead due to message passing and thread management.
+* May not be suitable for all types of concurrency problems.
+
+## Related Java Design Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Encapsulates a request as an object, similarly to how the Active Object pattern encapsulates method calls.
+* [Promise](https://java-design-patterns.com/patterns/promise/): Provides a means to retrieve the result of an asynchronous method call, often used in conjunction with Active Object.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): The Active Object pattern can use a proxy to handle method invocations asynchronously.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE)
+* [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
+* [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV)
+* [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61)
+* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
diff --git a/active-object/etc/active-object-sequence-diagram.png b/active-object/etc/active-object-sequence-diagram.png
new file mode 100644
index 000000000000..b725d9b07b6d
Binary files /dev/null and b/active-object/etc/active-object-sequence-diagram.png differ
diff --git a/active-object/etc/active-object.urm.png b/active-object/etc/active-object.urm.png
new file mode 100644
index 000000000000..c14f66144ee2
Binary files /dev/null and b/active-object/etc/active-object.urm.png differ
diff --git a/active-object/etc/active-object.urm.puml b/active-object/etc/active-object.urm.puml
new file mode 100644
index 000000000000..3fc3c8e1e921
--- /dev/null
+++ b/active-object/etc/active-object.urm.puml
@@ -0,0 +1,25 @@
+@startuml
+package com.iluwatar.activeobject {
+ abstract class ActiveCreature {
+ - logger : Logger
+ - name : String
+ - requests : BlockingQueue
+ - thread : Thread
+ + ActiveCreature(name : String)
+ + eat()
+ + name() : String
+ + roam()
+ }
+ class App {
+ - creatures : Integer
+ - logger : Logger
+ + App()
+ + main(args : String[]) {static}
+ + run()
+ }
+ class Orc {
+ + Orc(name : String)
+ }
+}
+Orc --|> ActiveCreature
+@enduml
\ No newline at end of file
diff --git a/active-object/pom.xml b/active-object/pom.xml
new file mode 100644
index 000000000000..aa26dbbe2e48
--- /dev/null
+++ b/active-object/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ active-object
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.activeobject.App
+
+
+
+
+
+
+
+
+
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
new file mode 100644
index 000000000000..5a440020c0ac
--- /dev/null
+++ b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
@@ -0,0 +1,118 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.activeobject;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** ActiveCreature class is the base of the active object example. */
+public abstract class ActiveCreature {
+
+ private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
+
+ private BlockingQueue requests;
+
+ private String name;
+
+ private Thread thread; // Thread of execution.
+
+ private int status; // status of the thread of execution.
+
+ /** Constructor and initialization. */
+ protected ActiveCreature(String name) {
+ this.name = name;
+ this.status = 0;
+ this.requests = new LinkedBlockingQueue<>();
+ thread =
+ new Thread(
+ () -> {
+ boolean infinite = true;
+ while (infinite) {
+ try {
+ requests.take().run();
+ } catch (InterruptedException e) {
+ if (this.status != 0) {
+ logger.error("Thread was interrupted. --> {}", e.getMessage());
+ }
+ infinite = false;
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+ thread.start();
+ }
+
+ /**
+ * Eats the porridge.
+ *
+ * @throws InterruptedException due to firing a new Runnable.
+ */
+ public void eat() throws InterruptedException {
+ requests.put(
+ () -> {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ });
+ }
+
+ /**
+ * Roam the wastelands.
+ *
+ * @throws InterruptedException due to firing a new Runnable.
+ */
+ public void roam() throws InterruptedException {
+ requests.put(() -> logger.info("{} has started to roam in the wastelands.", name()));
+ }
+
+ /**
+ * Returns the name of the creature.
+ *
+ * @return the name of the creature.
+ */
+ public String name() {
+ return this.name;
+ }
+
+ /**
+ * Kills the thread of execution.
+ *
+ * @param status of the thread of execution. 0 == OK, the rest is logging an error.
+ */
+ public void kill(int status) {
+ this.status = status;
+ this.thread.interrupt();
+ }
+
+ /**
+ * Returns the status of the thread of execution.
+ *
+ * @return the status of the thread of execution.
+ */
+ public int getStatus() {
+ return this.status;
+ }
+}
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/App.java b/active-object/src/main/java/com/iluwatar/activeobject/App.java
new file mode 100644
index 000000000000..ca3a5526ebb8
--- /dev/null
+++ b/active-object/src/main/java/com/iluwatar/activeobject/App.java
@@ -0,0 +1,75 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.activeobject;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Active Object pattern helps to solve synchronization difficulties without using
+ * 'synchronized' methods. The active object will contain a thread-safe data structure (such as
+ * BlockingQueue) and use to synchronize method calls by moving the logic of the method into an
+ * invocator(usually a Runnable) and store it in the DSA.
+ *
+ * In this example, we fire 20 threads to modify a value in the target class.
+ */
+public class App implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
+
+ private static final int NUM_CREATURES = 3;
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line arguments.
+ */
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
+ }
+
+ @Override
+ public void run() {
+ List creatures = new ArrayList<>();
+ try {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.add(new Orc(Orc.class.getSimpleName() + i));
+ creatures.get(i).eat();
+ creatures.get(i).roam();
+ }
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ Thread.currentThread().interrupt();
+ } finally {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.get(i).kill(0);
+ }
+ }
+ }
+}
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
new file mode 100644
index 000000000000..30adde034de5
--- /dev/null
+++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
@@ -0,0 +1,33 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.activeobject;
+
+/** An implementation of the ActiveCreature class. */
+public class Orc extends ActiveCreature {
+
+ public Orc(String name) {
+ super(name);
+ }
+}
diff --git a/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
new file mode 100644
index 000000000000..be79e2fb5527
--- /dev/null
+++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
@@ -0,0 +1,42 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.activeobject;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class ActiveCreatureTest {
+
+ @Test
+ void executionTest() throws InterruptedException {
+ ActiveCreature orc = new Orc("orc1");
+ assertEquals("orc1", orc.name());
+ assertEquals(0, orc.getStatus());
+ orc.eat();
+ orc.roam();
+ orc.kill(0);
+ }
+}
diff --git a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
new file mode 100644
index 000000000000..559e2a1f58f1
--- /dev/null
+++ b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.activeobject;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/actor-model/README.md b/actor-model/README.md
new file mode 100644
index 000000000000..be8065ffefef
--- /dev/null
+++ b/actor-model/README.md
@@ -0,0 +1,201 @@
+---
+title: "Actor Model Pattern in Java: Building Concurrent Systems with Elegance"
+shortTitle: Actor Model
+description: "Explore the Actor Model pattern in Java with real-world examples and practical implementation. Learn how to build scalable, message-driven systems using actors, messages, and asynchronous communication."
+category: Concurrency
+language: en
+tag:
+ - Concurrency
+ - Messaging
+ - Isolation
+ - Asynchronous
+ - Distributed Systems
+ - Actor Model
+---
+
+## Also Known As
+
+- Message-passing concurrency
+- Actor-based concurrency
+
+---
+
+## Intent of Actor Model Pattern
+
+The Actor Model pattern enables the construction of highly concurrent, distributed, and fault-tolerant systems by using isolated components (actors) that interact exclusively through asynchronous message passing.
+
+---
+
+## Detailed Explanation of Actor Model Pattern with Real-World Examples
+
+### 📦 Real-world Example
+
+Imagine a customer service system:
+- Each **customer support agent** is an **actor**.
+- Customers **send questions (messages)** to agents.
+- Each agent handles one request at a time and can **respond asynchronously** without interfering with other agents.
+
+---
+
+### 🧠 In Plain Words
+
+> "Actors are like independent workers that never share memory and only communicate through messages."
+
+---
+
+### 📖 Wikipedia Says
+
+> [Actor model](https://en.wikipedia.org/wiki/Actor_model) is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent computation.
+
+---
+
+### 🧹 Architecture Diagram
+
+
+
+---
+
+## Programmatic Example of Actor Model Pattern in Java
+
+### Actor.java
+
+```java
+public abstract class Actor implements Runnable {
+
+ @Setter @Getter private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active = true;
+
+
+ public void send(Message message) {
+ mailbox.add(message);
+ }
+
+ public void stop() {
+ active = false;
+ }
+
+ @Override
+ public void run() {
+
+ }
+
+ protected abstract void onReceive(Message message);
+}
+
+```
+
+### Message.java
+
+```java
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
+```
+
+### ActorSystem.java
+
+```java
+public class ActorSystem {
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
+```
+
+### App.java
+
+```java
+public class App {
+ public static void main(String[] args) {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
+```
+
+---
+
+## When to Use the Actor Model Pattern in Java
+
+- When building **concurrent or distributed systems**
+- When you want **no shared mutable state**
+- When you need **asynchronous, message-driven communication**
+- When components should be **isolated and loosely coupled**
+
+---
+
+## Actor Model Pattern Java Tutorials
+
+- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka)
+- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143)
+
+---
+
+## Real-World Applications of Actor Model Pattern in Java
+
+- [Akka Framework](https://akka.io/)
+- [Erlang and Elixir concurrency](https://www.erlang.org/)
+- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/)
+- JVM-based game engines and simulators
+
+---
+
+## Benefits and Trade-offs of Actor Model Pattern
+
+### ✅ Benefits
+- High concurrency support
+- Easy scaling across threads or machines
+- Fault isolation and recovery
+- Message ordering within actors
+
+### ⚠️ Trade-offs
+- Harder to debug due to asynchronous behavior
+- Slight performance overhead due to message queues
+- More complex to design than simple method calls
+
+---
+
+## Related Java Design Patterns
+
+- [Command Pattern](../command)
+- [Mediator Pattern](../mediator)
+- [Event-Driven Architecture](../event-driven-architecture)
+- [Observer Pattern](../observer)
+
+---
+
+## References and Credits
+
+- *Programming Erlang*, Joe Armstrong
+- *Reactive Design Patterns*, Roland Kuhn
+- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/)
+- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html)
+
diff --git a/actor-model/etc/Actor_Model_UML_Class_Diagram.png b/actor-model/etc/Actor_Model_UML_Class_Diagram.png
new file mode 100644
index 000000000000..a4c34d7a75fd
Binary files /dev/null and b/actor-model/etc/Actor_Model_UML_Class_Diagram.png differ
diff --git a/actor-model/etc/actor-model.urm.puml b/actor-model/etc/actor-model.urm.puml
new file mode 100644
index 000000000000..020c1fc735a4
--- /dev/null
+++ b/actor-model/etc/actor-model.urm.puml
@@ -0,0 +1,35 @@
+@startuml actor-model
+
+title Actor Model - UML Class Diagram
+
+class ActorSystem {
+ +actorOf(actor: Actor): Actor
+ +shutdown(): void
+}
+
+class Actor {
+ -mailbox: BlockingQueue
+ -active: boolean
+ +send(message: Message): void
+ +stop(): void
+ +run(): void
+ #onReceive(message: Message): void
+}
+
+class ExampleActor {
+ +onReceive(message: Message): void
+}
+
+class Message {
+ -content: String
+ -sender: Actor
+ +getContent(): String
+ +getSender(): Actor
+}
+
+ActorSystem --> Actor : creates
+Actor <|-- ExampleActor : extends
+Actor --> Message : processes
+ExampleActor --> Message : uses
+
+@enduml
diff --git a/actor-model/pom.xml b/actor-model/pom.xml
new file mode 100644
index 000000000000..76c288829b8d
--- /dev/null
+++ b/actor-model/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+
+ 4.0.0
+
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ actor-model
+ Actor Model
+
+
+
+
+
+ org.junit
+ junit-bom
+ 5.11.0
+ pom
+ import
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+ jar-with-dependencies
+
+
+
+ com.iluwatar.actormodel.App
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java
new file mode 100644
index 000000000000..6e2aaccd1937
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import lombok.Getter;
+import lombok.Setter;
+
+public abstract class Actor implements Runnable {
+
+ @Setter @Getter private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active =
+ true; // always read from main memory and written back to main memory,
+
+ // rather than being cached in a thread's local memory. To make it consistent to all Actors
+
+ public void send(Message message) {
+ mailbox.add(message); // Add message to queue
+ }
+
+ public void stop() {
+ active = false; // Stop the actor loop
+ }
+
+ @Override
+ public void run() {
+ while (active) {
+ try {
+ Message message = mailbox.take(); // Wait for a message
+ onReceive(message); // Process it
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ // Child classes must define what to do with a message
+ protected abstract void onReceive(Message message);
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java
new file mode 100644
index 000000000000..db7c21cb6088
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ActorSystem {
+ private final ExecutorService executor = Executors.newCachedThreadPool();
+ private final ConcurrentHashMap actorRegister = new ConcurrentHashMap<>();
+ private final AtomicInteger idCounter = new AtomicInteger(0);
+
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/App.java b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
new file mode 100644
index 000000000000..79fe79e48a6f
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.
+ */
+
+/**
+ * The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
+ * message-driven way.
+ *
+ * In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
+ * - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
+ * is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
+ * delivered asynchronously, and each actor processes them one at a time.
+ *
+ *
💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
+ * many actors - Suitable for highly concurrent or distributed systems
+ *
+ *
🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
+ * `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
+ * `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
+ * objects that include the message content and sender's ID. - The actors process messages
+ * **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
+ * run. - The system is shut down gracefully at the end.
+ */
+package com.iluwatar.actormodel;
+
+public class App {
+ public static void main(String[] args) throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
new file mode 100644
index 000000000000..fd49325f44bd
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ // Logger log = Logger.getLogger(getClass().getName());
+
+ @Override
+ protected void onReceive(Message message) {
+ LOGGER.info(
+ "[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
+ Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
+ // Reply of the message
+ if (sender != null && !message.getSenderId().equals(getActorId())) {
+ sender.send(new Message("I got your message ", getActorId()));
+ }
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
new file mode 100644
index 000000000000..037f96716558
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor2 extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor2(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ @Override
+ protected void onReceive(Message message) {
+ receivedMessages.add(message.getContent());
+ LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Message.java b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
new file mode 100644
index 000000000000..03ca6e02cac0
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
diff --git a/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
new file mode 100644
index 000000000000..a4a0dee569ab
--- /dev/null
+++ b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actor;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.actormodel.ActorSystem;
+import com.iluwatar.actormodel.App;
+import com.iluwatar.actormodel.ExampleActor;
+import com.iluwatar.actormodel.ExampleActor2;
+import com.iluwatar.actormodel.Message;
+import org.junit.jupiter.api.Test;
+
+public class ActorModelTest {
+ @Test
+ void testMainMethod() throws InterruptedException {
+ App.main(new String[] {});
+ }
+
+ @Test
+ public void testMessagePassing() throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+
+ ExampleActor srijan = new ExampleActor(system);
+ ExampleActor2 ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+
+ // Ansh recieves a message from Srijan
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+
+ // Wait briefly to allow async processing
+ Thread.sleep(200);
+
+ // Check that Srijan received the message
+ assertTrue(
+ ansh.getReceivedMessages().contains("Hello ansh"),
+ "ansh should receive the message from Srijan");
+ }
+}
diff --git a/acyclic-visitor/README.md b/acyclic-visitor/README.md
new file mode 100644
index 000000000000..fb57d5681fd4
--- /dev/null
+++ b/acyclic-visitor/README.md
@@ -0,0 +1,186 @@
+---
+title: "Acyclic Visitor Pattern in Java: Streamlining Object Interactions"
+shortTitle: Acyclic Visitor
+description: "Learn about the Acyclic Visitor pattern in Java. This guide explains how it decouples operations from object hierarchies, providing examples and real-world applications."
+category: Behavioral
+language: en
+tag:
+ - Decoupling
+ - Extensibility
+ - Interface
+ - Object composition
+---
+
+## Intent of Acyclic Visitor Design Pattern
+
+The Acyclic Visitor pattern in Java decouples operations from an object hierarchy, providing a flexible design for various applications.
+
+## Detailed Explanation of Acyclic Visitor Pattern with Real-World Examples
+
+Real-world example
+
+> An analogous real-world example of the Acyclic Visitor pattern in Java is a museum guide system, demonstrating the practical application of this design pattern. Imagine a museum with various exhibits like paintings, sculptures, and historical artifacts. The museum has different types of guides (audio guide, human guide, virtual reality guide) that provide information about each exhibit. Instead of modifying the exhibits every time a new guide type is introduced, each guide implements an interface to visit different exhibit types. This way, the museum can add new types of guides without altering the existing exhibits, ensuring that the system remains extensible and maintainable without forming any dependency cycles.
+
+In plain words
+
+> Acyclic Visitor allows functions to be added to existing class hierarchies without modifying the hierarchies.
+
+[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says
+
+> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
+
+Sequence diagram
+
+
+
+
+## Programmatic Example of Acyclic Visitor in Java
+
+In this Java example, we have a hierarchy of modem classes illustrating the Acyclic Visitor pattern. The modems in this hierarchy need to be visited by an external algorithm based on filtering criteria (is it Unix or DOS compatible modem).
+
+Here's the `Modem` hierarchy.
+
+```java
+public abstract class Modem {
+ public abstract void accept(ModemVisitor modemVisitor);
+}
+
+public class Zoom extends Modem {
+
+ // Other properties and methods...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof ZoomVisitor) {
+ ((ZoomVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+ }
+ }
+}
+
+public class Hayes extends Modem {
+
+ // Other properties and methods...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof HayesVisitor) {
+ ((HayesVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+ }
+ }
+}
+```
+
+Next, we introduce the `ModemVisitor` hierarchy.
+
+```java
+public interface ModemVisitor {
+}
+
+public interface HayesVisitor extends ModemVisitor {
+ void visit(Hayes hayes);
+}
+
+public interface ZoomVisitor extends ModemVisitor {
+ void visit(Zoom zoom);
+}
+
+public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
+}
+
+public class ConfigureForDosVisitor implements AllModemVisitor {
+
+ // Other properties and methods...
+
+ @Override
+ public void visit(Hayes hayes) {
+ LOGGER.info(hayes + " used with Dos configurator.");
+ }
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Dos configurator.");
+ }
+}
+
+public class ConfigureForUnixVisitor implements ZoomVisitor {
+
+ // Other properties and methods...
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Unix configurator.");
+ }
+}
+```
+
+Finally, here are the visitors in action.
+
+```java
+public static void main(String[] args) {
+ var conUnix = new ConfigureForUnixVisitor();
+ var conDos = new ConfigureForDosVisitor();
+
+ var zoom = new Zoom();
+ var hayes = new Hayes();
+
+ hayes.accept(conDos); // Hayes modem with Dos configurator
+ zoom.accept(conDos); // Zoom modem with Dos configurator
+ hayes.accept(conUnix); // Hayes modem with Unix configurator
+ zoom.accept(conUnix); // Zoom modem with Unix configurator
+}
+```
+
+Program output:
+
+```
+09:15:11.125 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Hayes modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Zoom modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.Hayes -- Only HayesVisitor is allowed to visit Hayes modem
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
+```
+
+## When to Use the Acyclic Visitor Pattern in Java
+
+This pattern can be used:
+
+* When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
+* When there are functions that operate upon a hierarchy, but which do not belong in the hierarchy itself. e.g. the ConfigureForDOS / ConfigureForUnix / ConfigureForX issue.
+* When you need to perform very different operations on an object depending upon its type.
+* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
+* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
+
+## Acyclic Visitor Pattern Java Tutorials
+
+* [The Acyclic Visitor Pattern (Code Crafter)](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
+
+## Benefits and Trade-offs of Acyclic Visitor Pattern
+
+Benefits:
+
+* Extensible: New operations can be added easily without changing the object structure.
+* Decoupled: Reduces coupling between the objects and the operations on them.
+* No dependency cycles: Ensures acyclic dependencies, improving maintainability and reducing complexity.
+
+Trade-offs:
+
+* Increased complexity: Can introduce additional complexity with the need for multiple visitor interfaces.
+* Maintenance overhead: Modifying the object hierarchy requires updating all visitors.
+
+## Related Java Design Patterns
+
+* [Composite](https://java-design-patterns.com/patterns/composite/): Often used in conjunction with Acyclic Visitor to allow treating individual objects and compositions uniformly.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used alongside to add responsibilities to objects dynamically.
+* [Visitor](https://java-design-patterns.com/patterns/visitor/): The Acyclic Visitor pattern is a variation of the Visitor pattern that avoids cyclic dependencies.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
+* [Acyclic Visitor (Robert C. Martin)](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
+* [Acyclic Visitor (WikiWikiWeb)](https://wiki.c2.com/?AcyclicVisitor)
diff --git a/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png
new file mode 100644
index 000000000000..a3c2ba56b89f
Binary files /dev/null and b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png differ
diff --git a/acyclic-visitor/etc/acyclic-visitor.png b/acyclic-visitor/etc/acyclic-visitor.png
new file mode 100644
index 000000000000..7b4df13d80f8
Binary files /dev/null and b/acyclic-visitor/etc/acyclic-visitor.png differ
diff --git a/acyclic-visitor/etc/acyclic-visitor.urm.puml b/acyclic-visitor/etc/acyclic-visitor.urm.puml
new file mode 100644
index 000000000000..e67bbde4407c
--- /dev/null
+++ b/acyclic-visitor/etc/acyclic-visitor.urm.puml
@@ -0,0 +1,53 @@
+@startuml
+package com.iluwatar.acyclicvisitor {
+ interface AllModemVisitor {
+ }
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class ConfigureForDosVisitor {
+ - LOGGER : Logger {static}
+ + ConfigureForDosVisitor()
+ + visit(hayes : Hayes)
+ + visit(zoom : Zoom)
+ }
+ class ConfigureForUnixVisitor {
+ - LOGGER : Logger {static}
+ + ConfigureForUnixVisitor()
+ + visit(zoom : Zoom)
+ }
+ class Hayes {
+ - LOGGER : Logger {static}
+ + Hayes()
+ + accept(modemVisitor : ModemVisitor)
+ + toString() : String
+ }
+ interface HayesVisitor {
+ + visit(Hayes) {abstract}
+ }
+ abstract class Modem {
+ + Modem()
+ + accept(ModemVisitor) {abstract}
+ }
+ interface ModemVisitor {
+ }
+ class Zoom {
+ - LOGGER : Logger {static}
+ + Zoom()
+ + accept(modemVisitor : ModemVisitor)
+ + toString() : String
+ }
+ interface ZoomVisitor {
+ + visit(Zoom) {abstract}
+ }
+}
+AllModemVisitor --|> ZoomVisitor
+AllModemVisitor --|> HayesVisitor
+ConfigureForDosVisitor ..|> AllModemVisitor
+ConfigureForUnixVisitor ..|> ZoomVisitor
+Hayes --|> Modem
+HayesVisitor --|> ModemVisitor
+Zoom --|> Modem
+ZoomVisitor --|> ModemVisitor
+@enduml
\ No newline at end of file
diff --git a/acyclic-visitor/pom.xml b/acyclic-visitor/pom.xml
new file mode 100644
index 000000000000..b4f5646b71c0
--- /dev/null
+++ b/acyclic-visitor/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ acyclic-visitor
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.acyclicvisitor.App
+
+
+
+
+
+
+
+
+
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
new file mode 100644
index 000000000000..a3b1679a2d9f
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+/**
+ * All ModemVisitor interface extends all visitor interfaces. This interface provides ease of use
+ * when a visitor needs to visit all modem types.
+ */
+public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
new file mode 100644
index 000000000000..3b7c6cd61e4b
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+/**
+ * The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies
+ * without affecting those hierarchies, and without creating the dependency cycles that are inherent
+ * to the GoF Visitor pattern, by making the Visitor base class degenerate
+ *
+ * In this example the visitor base class is {@link ModemVisitor}. The base class of the visited
+ * hierarchy is {@link Modem} and has two children {@link Hayes} and {@link Zoom} each one having
+ * its own visitor interface {@link HayesVisitor} and {@link ZoomVisitor} respectively. {@link
+ * ConfigureForUnixVisitor} and {@link ConfigureForDosVisitor} implement each derivative's visit
+ * method only if it is required
+ */
+public class App {
+
+ /** Program's entry point. */
+ public static void main(String[] args) {
+ var conUnix = new ConfigureForUnixVisitor();
+ var conDos = new ConfigureForDosVisitor();
+
+ var zoom = new Zoom();
+ var hayes = new Hayes();
+
+ hayes.accept(conDos); // Hayes modem with Dos configurator
+ zoom.accept(conDos); // Zoom modem with Dos configurator
+ hayes.accept(conUnix); // Hayes modem with Unix configurator
+ zoom.accept(conUnix); // Zoom modem with Unix configurator
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
new file mode 100644
index 000000000000..267a8d66ac45
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
+ */
+@Slf4j
+public class ConfigureForDosVisitor implements AllModemVisitor {
+
+ @Override
+ public void visit(Hayes hayes) {
+ LOGGER.info(hayes + " used with Dos configurator.");
+ }
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Dos configurator.");
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
new file mode 100644
index 000000000000..d9fd14f69435
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ConfigureForUnixVisitor class implements zoom's visit method for Unix manufacturer, unlike
+ * traditional visitor pattern, this class may selectively implement visit for other modems.
+ */
+@Slf4j
+public class ConfigureForUnixVisitor implements ZoomVisitor {
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Unix configurator.");
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
new file mode 100644
index 000000000000..e0b2fcc2b530
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
@@ -0,0 +1,48 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** Hayes class implements its accept method. */
+@Slf4j
+public class Hayes implements Modem {
+
+ /** Accepts all visitors but honors only HayesVisitor. */
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof HayesVisitor) {
+ ((HayesVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+ }
+ }
+
+ /** Hayes' modem's toString method. */
+ @Override
+ public String toString() {
+ return "Hayes modem";
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
new file mode 100644
index 000000000000..aad9b970994f
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+/** HayesVisitor interface. */
+public interface HayesVisitor extends ModemVisitor {
+ void visit(Hayes hayes);
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
new file mode 100644
index 000000000000..8552574453e5
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+/** //Modem abstract class. converted to an interface */
+public interface Modem {
+ void accept(ModemVisitor modemVisitor);
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
new file mode 100644
index 000000000000..9391a980cd29
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
@@ -0,0 +1,33 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+/**
+ * ModemVisitor interface does not contain any visit methods so that it does not depend on the
+ * visited hierarchy. Each derivative's visit method is declared in its own visitor interface
+ */
+public interface ModemVisitor {
+ // Visitor is a degenerate base class for all visitors.
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
new file mode 100644
index 000000000000..59b50a54a12f
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
@@ -0,0 +1,48 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** Zoom class implements its accept method. */
+@Slf4j
+public class Zoom implements Modem {
+
+ /** Accepts all visitors but honors only ZoomVisitor. */
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof ZoomVisitor) {
+ ((ZoomVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+ }
+ }
+
+ /** Zoom modem's toString method. */
+ @Override
+ public String toString() {
+ return "Zoom modem";
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
new file mode 100644
index 000000000000..5388ded6f735
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+/** ZoomVisitor interface. */
+public interface ZoomVisitor extends ModemVisitor {
+ void visit(Zoom zoom);
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
new file mode 100644
index 000000000000..7a21498a63ea
--- /dev/null
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that the Acyclic Visitor example runs without errors. */
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
new file mode 100644
index 000000000000..a989d9287921
--- /dev/null
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.Test;
+
+/** Hayes test class */
+class HayesTest {
+
+ @Test
+ void testAcceptForDos() {
+ var hayes = new Hayes();
+ var mockVisitor = mock(ConfigureForDosVisitor.class);
+
+ hayes.accept(mockVisitor);
+ verify((HayesVisitor) mockVisitor).visit(eq(hayes));
+ }
+
+ @Test
+ void testAcceptForUnix() {
+ var hayes = new Hayes();
+ var mockVisitor = mock(ConfigureForUnixVisitor.class);
+
+ hayes.accept(mockVisitor);
+
+ verifyNoMoreInteractions(mockVisitor);
+ }
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
new file mode 100644
index 000000000000..d5fe79965d47
--- /dev/null
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.acyclicvisitor;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.jupiter.api.Test;
+
+/** Zoom test class */
+class ZoomTest {
+
+ @Test
+ void testAcceptForDos() {
+ var zoom = new Zoom();
+ var mockVisitor = mock(ConfigureForDosVisitor.class);
+
+ zoom.accept(mockVisitor);
+ verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
+ }
+
+ @Test
+ void testAcceptForUnix() {
+ var zoom = new Zoom();
+ var mockVisitor = mock(ConfigureForUnixVisitor.class);
+
+ zoom.accept(mockVisitor);
+ verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
+ }
+}
diff --git a/adapter/README.md b/adapter/README.md
index 6de04a72c580..489742494709 100644
--- a/adapter/README.md
+++ b/adapter/README.md
@@ -1,31 +1,31 @@
---
-layout: pattern
-title: Adapter
-folder: adapter
-permalink: /patterns/adapter/
-pumlid: DSR14S8m30J0Lg20M7-wEMnDOiPMFDA9j0yyUEtUkzMHJTF7xI1NF4GSLzaxZtncgDVJgCPIpobzv0N2vOKtjgRHTziMI7KBcOXl10thfxB-Nz9dMJd71m00
-categories: Structural
-tags:
- - Java
- - Gang Of Four
- - Difficulty-Beginner
+title: "Adapter Pattern in Java: Seamless Integration of Incompatible Systems"
+shortTitle: Adapter
+description: "Learn how the Adapter Design Pattern works in Java with detailed examples and use cases. Understand how it enables compatibility between incompatible interfaces."
+category: Structural
+language: en
+tag:
+ - Compatibility
+ - Decoupling
+ - Gang of Four
+ - Interface
+ - Object composition
+ - Wrapping
---
## Also known as
-Wrapper
-## Intent
-Convert the interface of a class into another interface the clients
-expect. Adapter lets classes work together that couldn't otherwise because of
-incompatible interfaces.
+* Wrapper
-## Explanation
+## Intent of Adapter Design Pattern
-Real world example
+The Adapter Design Pattern in Java converts the interface of a class into another interface that clients expect, enabling compatibility.
-> Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter.
-> Another example would be the famous power adapter; a three legged plug can't be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet.
-> Yet another example would be a translator translating words spoken by one person to another
+## Detailed Explanation of Adapter Pattern with Real-World Examples
+
+Real-world example
+
+> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets. Yet another example would be a translator translating words spoken by one person to another
In plain words
@@ -35,100 +35,120 @@ Wikipedia says
> In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
-**Programmatic Example**
+Sequence diagram
-Consider a captain that can only use rowing boats and cannot sail at all.
+
-First we have interfaces `RowingBoat` and `FishingBoat`
+## Programmatic Example of Adapter Pattern in Java
-```
+The Adapter Pattern example in Java shows how a class with an incompatible interface can be adapted to work with another class.
+
+Consider a wannabe captain that can only use rowing boats but can't sail at all.
+
+First, we have interfaces `RowingBoat` and `FishingBoat`
+
+```java
public interface RowingBoat {
- void row();
+ void row();
}
+@Slf4j
public class FishingBoat {
- private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
- public void sail() {
- LOGGER.info("The fishing boat is sailing");
- }
+ public void sail() {
+ LOGGER.info("The fishing boat is sailing");
+ }
}
```
-And captain expects an implementation of `RowingBoat` interface to be able to move
+The captain expects an implementation of `RowingBoat` interface to be able to move.
-```
-public class Captain implements RowingBoat {
+```java
+public class Captain {
- private RowingBoat rowingBoat;
+ private final RowingBoat rowingBoat;
- public Captain(RowingBoat rowingBoat) {
- this.rowingBoat = rowingBoat;
- }
+ // default constructor and setter for rowingBoat
+ public Captain(RowingBoat rowingBoat) {
+ this.rowingBoat = rowingBoat;
+ }
- @Override
- public void row() {
- rowingBoat.row();
- }
+ public void row() {
+ rowingBoat.row();
+ }
}
```
-Now let's say the pirates are coming and our captain needs to escape but there is only fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
+Now, let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
-```
+```java
+@Slf4j
public class FishingBoatAdapter implements RowingBoat {
- private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);
+ private final FishingBoat boat;
+
+ public FishingBoatAdapter() {
+ boat = new FishingBoat();
+ }
- private FishingBoat boat;
+ @Override
+ public void row() {
+ boat.sail();
+ }
+}
+```
- public FishingBoatAdapter() {
- boat = new FishingBoat();
- }
+Now the `Captain` can use the `FishingBoat` to escape the pirates.
- @Override
- public void row() {
- boat.sail();
- }
+```java
+ public static void main(final String[] args) {
+ // The captain can only operate rowing boats but with adapter he is able to
+ // use fishing boats as well
+ var captain = new Captain(new FishingBoatAdapter());
+ captain.row();
}
```
-And now the `Captain` can use the `FishingBoat` to escape the pirates.
+The program outputs:
```
-Captain captain = new Captain(new FishingBoatAdapter());
-captain.row();
+10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
```
-## Applicability
-Use the Adapter pattern when
+## When to Use the Adapter Pattern in Java
+
+Use the Adapter pattern in Java when
-* you want to use an existing class, and its interface does not match the one you need
-* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
-* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
-* most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
+* You want to use an existing class, and its interface does not match the one you need
+* You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
+* You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class.
+* Most of the applications using third-party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
-## Consequences:
-Class and object adapters have different trade-offs. A class adapter
+## Adapter Pattern Java Tutorials
-* adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter won’t work when we want to adapt a class and all its subclasses.
-* let’s Adapter override some of Adaptee’s behavior, since Adapter is a subclass of Adaptee.
-* introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
+* [Using the Adapter Design Pattern in Java (Dzone)](https://dzone.com/articles/adapter-design-pattern-in-java)
+* [Adapter in Java (Refactoring Guru)](https://refactoring.guru/design-patterns/adapter/java/example)
+* [The Adapter Pattern in Java (Baeldung)](https://www.baeldung.com/java-adapter-pattern)
+* [Adapter Design Pattern (GeeksForGeeks)](https://www.geeksforgeeks.org/adapter-pattern/)
-An object adapter
+## Benefits and Trade-offs of Adapter Pattern
-* let’s a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
-* makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself.
+Class and object adapters offer different benefits and drawbacks. A class adapter adapts the Adaptee to the Target by binding to a specific Adaptee class, which means it cannot adapt a class and all its subclasses. This type of adapter allows the Adapter to override some of the Adaptee’s behavior because the Adapter is a subclass of the Adaptee. Additionally, it introduces only one object without needing extra pointer indirection to reach the Adaptee.
+On the other hand, an object adapter allows a single Adapter to work with multiple Adaptees, including the Adaptee and all its subclasses. This type of adapter can add functionality to all Adaptees simultaneously. However, it makes overriding the Adaptee’s behavior more difficult, as it requires subclassing the Adaptee and having the Adapter refer to this subclass instead of the Adaptee itself.
-## Real world examples
+## Real-World Applications of Adapter Pattern in Java
+* `java.io.InputStreamReader` and `java.io.OutputStreamWriter` in the Java IO library.
+* GUI component libraries that allow for plug-ins or adapters to convert between different GUI component interfaces.
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
+## References and Credits
-## Credits
-
-* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
-* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
diff --git a/adapter/etc/adapter-sequence-diagram.png b/adapter/etc/adapter-sequence-diagram.png
new file mode 100644
index 000000000000..a9bb557ea61e
Binary files /dev/null and b/adapter/etc/adapter-sequence-diagram.png differ
diff --git a/adapter/etc/adapter.urm.png b/adapter/etc/adapter.urm.png
new file mode 100644
index 000000000000..341ad67699d9
Binary files /dev/null and b/adapter/etc/adapter.urm.png differ
diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml
new file mode 100644
index 000000000000..1277cbb87125
--- /dev/null
+++ b/adapter/etc/adapter.urm.puml
@@ -0,0 +1,31 @@
+@startuml
+package com.iluwatar.adapter {
+ class App {
+ - App()
+ + main(args : String[]) {static}
+ }
+ class Captain {
+ - rowingBoat : RowingBoat
+ + Captain()
+ + Captain(boat : RowingBoat)
+ ~ row()
+ ~ setRowingBoat(boat : RowingBoat)
+ }
+ ~class FishingBoat {
+ - LOGGER : Logger {static}
+ ~ FishingBoat()
+ ~ sail()
+ }
+ class FishingBoatAdapter {
+ - boat : FishingBoat
+ + FishingBoatAdapter()
+ + row()
+ }
+ interface RowingBoat {
+ + row() {abstract}
+ }
+}
+FishingBoatAdapter --> "-boat" FishingBoat
+Captain --> "-rowingBoat" RowingBoat
+FishingBoatAdapter ..|> RowingBoat
+@enduml
\ No newline at end of file
diff --git a/adapter/pom.xml b/adapter/pom.xml
index 24245fad72c8..6e7f45a515b5 100644
--- a/adapter/pom.xml
+++ b/adapter/pom.xml
@@ -1,8 +1,10 @@
-
+
-
+
4.0.0
com.iluwatar
java-design-patterns
- 1.18.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
adapter
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
@@ -44,4 +53,25 @@
test
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.adapter.App
+
+
+
+
+
+
+
+
diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java
index a624cc38f264..a4fa74274f74 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/App.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/App.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -28,33 +30,32 @@
* The Adapter design pattern allows otherwise incompatible classes to work together by converting
* the interface of one class into an interface expected by the clients.
*
- *
- * There are two variations of the Adapter pattern: The class adapter implements the adaptee's
+ *
There are two variations of the Adapter pattern: The class adapter implements the adaptee's
* interface whereas the object adapter uses composition to contain the adaptee in the adapter
* object. This example uses the object adapter approach.
*
- *
- * The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class (
- * {@link FishingBoat}) into a suitable one expected by the client ( {@link RowingBoat} ).
+ *
The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class ({@link
+ * FishingBoat}) into a suitable one expected by the client ({@link RowingBoat}).
*
- *
- * The story of this implementation is this.
+ *
The story of this implementation is this.
* Pirates are coming! we need a {@link RowingBoat} to flee! We have a {@link FishingBoat} and our
* captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
- * captain needs a rowing boat which he can operate. The spec is in {@link RowingBoat}. We will
- * use the Adapter pattern to reuse {@link FishingBoat}.
- *
+ * captain needs a rowing boat which he can operate. The spec is in {@link RowingBoat}. We will use
+ * the Adapter pattern to reuse {@link FishingBoat}.
*/
-public class App {
+public final class App {
+
+ private App() {}
/**
* Program entry point.
*
* @param args command line args
*/
- public static void main(String[] args) {
- // The captain can only operate rowing boats but with adapter he is able to use fishing boats as well
- Captain captain = new Captain(new FishingBoatAdapter());
+ public static void main(final String[] args) {
+ // The captain can only operate rowing boats but with adapter he is able to
+ // use fishing boats as well
+ var captain = new Captain(new FishingBoatAdapter());
captain.row();
}
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
index 3690169800d2..3b771e9d833e 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/Captain.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,27 +24,22 @@
*/
package com.iluwatar.adapter;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
/**
* The Captain uses {@link RowingBoat} to sail.
* This is the client in the pattern.
*/
-public class Captain implements RowingBoat {
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public final class Captain {
private RowingBoat rowingBoat;
- public Captain() {}
-
- public Captain(RowingBoat rowingBoat) {
- this.rowingBoat = rowingBoat;
- }
-
- public void setRowingBoat(RowingBoat rowingBoat) {
- this.rowingBoat = rowingBoat;
- }
-
- @Override
- public void row() {
+ void row() {
rowingBoat.row();
}
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
index c46814d18f16..dd39f88f1ce0 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,21 +24,16 @@
*/
package com.iluwatar.adapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
- *
- * Device class (adaptee in the pattern). We want to reuse this class.
- * Fishing boat moves by sailing.
- *
+ * Device class (adaptee in the pattern). We want to reuse this class. Fishing boat moves by
+ * sailing.
*/
-public class FishingBoat {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
+@Slf4j
+final class FishingBoat {
- public void sail() {
+ void sail() {
LOGGER.info("The fishing boat is sailing");
}
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
index 1e758e917040..8c7e9e88e220 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -23,21 +25,14 @@
package com.iluwatar.adapter;
/**
- *
* Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link RowingBoat}
* interface expected by the client ({@link Captain}).
- *
*/
public class FishingBoatAdapter implements RowingBoat {
- private FishingBoat boat;
-
- public FishingBoatAdapter() {
- boat = new FishingBoat();
- }
+ private final FishingBoat boat = new FishingBoat();
- @Override
- public void row() {
+ public final void row() {
boat.sail();
}
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
index a9ca9ad39717..55eeeaf4bd42 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -25,10 +27,8 @@
/**
* The interface expected by the client.
* A rowing boat is rowed to move.
- *
*/
public interface RowingBoat {
void row();
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/package-info.java b/adapter/src/main/java/com/iluwatar/adapter/package-info.java
new file mode 100644
index 000000000000..c42f2ac86ceb
--- /dev/null
+++ b/adapter/src/main/java/com/iluwatar/adapter/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.adapter;
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
index 9938f055963a..bc4984da3d6f 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,20 +24,16 @@
*/
package com.iluwatar.adapter;
-import org.junit.Before;
-import org.junit.Test;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-/**
- * Test class
- *
- */
-public class AdapterPatternTest {
+/** Tests for the adapter pattern. */
+class AdapterPatternTest {
private Map beans;
@@ -43,36 +41,34 @@ public class AdapterPatternTest {
private static final String ROWING_BEAN = "captain";
- /**
- * This method runs before the test execution and sets the bean objects in the beans Map.
- */
- @Before
- public void setup() {
+ /** This method runs before the test execution and sets the bean objects in the beans Map. */
+ @BeforeEach
+ void setup() {
beans = new HashMap<>();
- FishingBoatAdapter fishingBoatAdapter = spy(new FishingBoatAdapter());
+ var fishingBoatAdapter = spy(new FishingBoatAdapter());
beans.put(FISHING_BEAN, fishingBoatAdapter);
- Captain captain = new Captain();
+ var captain = new Captain();
captain.setRowingBoat((FishingBoatAdapter) beans.get(FISHING_BEAN));
beans.put(ROWING_BEAN, captain);
}
/**
- * This test asserts that when we use the row() method on a captain bean(client), it is
- * internally calling sail method on the fishing boat object. The Adapter ({@link FishingBoatAdapter}
- * ) converts the interface of the target class ( {@link FishingBoat}) into a suitable one
- * expected by the client ({@link Captain} ).
+ * This test asserts that when we use the row() method on a captain bean(client), it is internally
+ * calling sail method on the fishing boat object. The Adapter ({@link FishingBoatAdapter} )
+ * converts the interface of the target class ( {@link FishingBoat}) into a suitable one expected
+ * by the client ({@link Captain} ).
*/
@Test
- public void testAdapter() {
- RowingBoat captain = (RowingBoat) beans.get(ROWING_BEAN);
+ void testAdapter() {
+ var captain = (Captain) beans.get(ROWING_BEAN);
// when captain moves
captain.row();
// the captain internally calls the battleship object to move
- RowingBoat adapter = (RowingBoat) beans.get(FISHING_BEAN);
+ var adapter = (RowingBoat) beans.get(FISHING_BEAN);
verify(adapter).row();
}
}
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
index 213035627615..a2cc4c22bcaa 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,17 +24,17 @@
*/
package com.iluwatar.adapter;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import java.io.IOException;
+import org.junit.jupiter.api.Test;
-/**
- * Tests that Adapter example runs without errors.
- */
-public class AppTest {
+/** Tests that Adapter example runs without errors. */
+class AppTest {
+
+ /** Check whether the execution of the main method in {@link App} throws an exception. */
@Test
- public void test() throws IOException {
- String[] args = {};
- App.main(args);
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md
deleted file mode 100644
index 75fe373fd301..000000000000
--- a/aggregator-microservices/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: pattern
-title: Aggregator Microservices
-folder: aggregator-microservices
-permalink: /patterns/aggregator-microservices/
-pumlid: JOov3SCm301NIGQGs7iRXYPa1g8ayB7NjuiKwGvtmBrbKC-Tq_hhY5Y-0HXUjKaS-Kbdepc2HrIQ2jBpma23BvvOTdPfeooCO1iEYlu0O6l63MDQKI6Rp-CKOWSE-ey_NzEqhjH-0m00
-categories: Architectural
-tags:
-- Java
-- Spring
----
-
-## Intent
-
-The user makes a single call to the Aggregator, and the aggregator then calls each relevant microservice and collects
-the data, apply business logic to it, and further publish is as a REST Endpoint.
-More variations of the aggregator are:
-- Proxy Microservice Design Pattern: A different microservice is called upon the business need.
-- Chained Microservice Design Pattern: In this case each microservice is dependent/ chained to a series
-of other microservices.
-
-
-
-## Applicability
-
-Use the Aggregator Microservices pattern when you need a unified API for various microservices, regardless the client device.
-
-## Credits
-
-* [Microservice Design Patterns](http://blog.arungupta.me/microservice-design-patterns/)
diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml
deleted file mode 100644
index 32d5caa3f2eb..000000000000
--- a/aggregator-microservices/aggregator-service/pom.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- 4.0.0
-
- aggregator-service
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
-
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- junit
- junit
- test
-
-
- org.mockito
- mockito-core
- test
-
-
- org.apache.httpcomponents
- httpclient
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- ${spring-boot.version}
-
-
-
- repackage
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java
deleted file mode 100644
index 9f1e4f4daeb8..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.aggregator.microservices;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.annotation.Resource;
-
-/**
- * The aggregator aggregates calls on various micro-services, collects
- * data and further publishes them under a REST endpoint.
- */
-@RestController
-public class Aggregator {
-
-
- @Resource
- private ProductInformationClient informationClient;
-
- @Resource
- private ProductInventoryClient inventoryClient;
-
-
- /**
- * Retrieves product data.
- *
- * @return a Product.
- */
- @RequestMapping("/product")
- public Product getProduct() {
- Product product = new Product();
- product.setTitle(informationClient.getProductTitle());
- product.setProductInventories(inventoryClient.getProductInventories());
- return product;
- }
-
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java
deleted file mode 100644
index c43112c2e00a..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.aggregator.microservices;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * Spring Boot EntryPoint Class
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
deleted file mode 100644
index 1ded980f90ea..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.aggregator.microservices;
-
-/**
- * Encapsulates all the data for a Product that clients will request.
- */
-public class Product {
-
- /**
- * The title of the product.
- */
- private String title;
-
-
- /**
- * The inventories of the product.
- */
- private int productInventories;
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public int getProductInventories() {
- return productInventories;
- }
-
- public void setProductInventories(int productInventories) {
- this.productInventories = productInventories;
- }
-
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java
deleted file mode 100644
index 16849d529386..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.aggregator.microservices;
-
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import java.io.IOException;
-
-/**
- * An adapter to communicate with information micro-service.
- */
-@Component
-public class ProductInformationClientImpl implements ProductInformationClient {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ProductInformationClientImpl.class);
-
- @Override
- public String getProductTitle() {
- String response = null;
- try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
- HttpGet httpGet = new HttpGet("http://localhost:51515/information");
- try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
- response = EntityUtils.toString(httpResponse.getEntity());
- }
- } catch (IOException e) {
- LOGGER.error("Exception caught.", e);
- }
- return response;
- }
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java
deleted file mode 100644
index 89f1a25e0580..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.aggregator.microservices;
-
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import java.io.IOException;
-
-/**
- * An adapter to communicate with inventory micro-service.
- */
-@Component
-public class ProductInventoryClientImpl implements ProductInventoryClient {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ProductInventoryClientImpl.class);
-
- @Override
- public int getProductInventories() {
- String response = "0";
- try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
- HttpGet httpGet = new HttpGet("http://localhost:51516/inventories");
- try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
- response = EntityUtils.toString(httpResponse.getEntity());
- }
- } catch (IOException e) {
- LOGGER.error("Exception caught.", e);
- }
- return Integer.parseInt(response);
- }
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/resources/application.properties b/aggregator-microservices/aggregator-service/src/main/resources/application.properties
deleted file mode 100644
index ae91c5eb5625..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright (c) 2014-2016 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.
-#
-
-server.port=50004
\ No newline at end of file
diff --git a/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java b/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java
deleted file mode 100644
index 2955f6781d92..000000000000
--- a/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.aggregator.microservices;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Test Aggregation of domain objects
- */
-public class AggregatorTest {
-
- @InjectMocks
- private Aggregator aggregator;
-
- @Mock
- private ProductInformationClient informationClient;
-
- @Mock
- private ProductInventoryClient inventoryClient;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- /**
- * Tests getting the data for a desktop client
- */
- @Test
- public void testGetProduct() {
- String title = "The Product Title.";
- int inventories = 5;
-
- when(informationClient.getProductTitle()).thenReturn(title);
- when(inventoryClient.getProductInventories()).thenReturn(inventories);
-
- Product testProduct = aggregator.getProduct();
-
- assertEquals(title, testProduct.getTitle());
- assertEquals(inventories, testProduct.getProductInventories());
- }
-
-}
diff --git a/aggregator-microservices/etc/aggregator-microservice.png b/aggregator-microservices/etc/aggregator-microservice.png
deleted file mode 100644
index ad344a7e1e0f..000000000000
Binary files a/aggregator-microservices/etc/aggregator-microservice.png and /dev/null differ
diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml
deleted file mode 100644
index 8b24cdcc0f10..000000000000
--- a/aggregator-microservices/information-microservice/pom.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- 4.0.0
-
- information-microservice
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
-
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- junit
- junit
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- ${spring-boot.version}
-
-
-
- repackage
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/information-microservice/src/main/resources/application.properties b/aggregator-microservices/information-microservice/src/main/resources/application.properties
deleted file mode 100644
index a921ea0fd100..000000000000
--- a/aggregator-microservices/information-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright (c) 2014-2016 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.
-#
-
-server.port=51515
\ No newline at end of file
diff --git a/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java b/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java
deleted file mode 100644
index 127dd39563eb..000000000000
--- a/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.information.microservice;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Test for Information Rest Controller
- */
-public class InformationControllerTest {
-
- @Test
- public void shouldGetProductTitle() {
- InformationController infoController = new InformationController();
-
- String title = infoController.getProductTitle();
-
- Assert.assertEquals("The Product Title.", title);
- }
-
-}
diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml
deleted file mode 100644
index 06804343cd99..000000000000
--- a/aggregator-microservices/inventory-microservice/pom.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- 4.0.0
-
- inventory-microservice
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
-
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- junit
- junit
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- ${spring-boot.version}
-
-
-
- repackage
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties
deleted file mode 100644
index 46b49a8a1464..000000000000
--- a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright (c) 2014-2016 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.
-#
-
-server.port=51516
\ No newline at end of file
diff --git a/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java b/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java
deleted file mode 100644
index b04370fba22b..000000000000
--- a/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.inventory.microservice;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Test Inventory Rest Controller
- */
-public class InventoryControllerTest {
- @Test
- public void testGetProductInventories() throws Exception {
- InventoryController inventoryController = new InventoryController();
-
- int numberOfInventories = inventoryController.getProductInventories();
-
- Assert.assertEquals(5, numberOfInventories);
- }
-}
diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml
deleted file mode 100644
index aae6b88d5a26..000000000000
--- a/aggregator-microservices/pom.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- 4.0.0
- aggregator-microservices
- pom
-
-
-
-
- com.github.markusmo3.urm
- urm-maven-plugin
- ${urm.version}
-
- ${project.basedir}/../etc
-
- com.iluwatar
-
-
-
- aggregator-microservices
-
-
-
-
-
-
-
- information-microservice
- aggregator-service
- inventory-microservice
-
-
diff --git a/ambassador/README.md b/ambassador/README.md
new file mode 100644
index 000000000000..fa08223feb75
--- /dev/null
+++ b/ambassador/README.md
@@ -0,0 +1,236 @@
+---
+title: "Ambassador Pattern in Java: Simplifying Remote Resource Management"
+shortTitle: Ambassador
+description: "Explore the Ambassador Pattern in Java, its benefits, use cases, and practical examples. Learn how to decouple and offload common functionalities to improve system performance and maintainability."
+category: Integration
+language: en
+tag:
+ - API design
+ - Decoupling
+ - Fault tolerance
+ - Proxy
+ - Resilience
+ - Scalability
+---
+
+## Intent of Ambassador Design Pattern
+
+The Ambassador Pattern in Java helps offload common functionalities such as monitoring, logging, and routing from a shared resource to a helper service instance, enhancing performance and maintainability in distributed systems.
+
+## Detailed Explanation of Ambassador Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy hotel where guests frequently request restaurant reservations, event tickets, or transportation arrangements. Instead of each guest individually contacting these services, the hotel provides a concierge. The concierge handles these tasks on behalf of the guests, ensuring that reservations are made smoothly, tickets are booked on time, and transportation is scheduled efficiently.
+>
+> In this analogy, the guests are the client services, the external providers (restaurants, ticket vendors, transportation) are the remote services, and the concierge represents the ambassador service. This setup allows the guests to focus on enjoying their stay while the concierge manages the complexities of external interactions, providing a seamless and enhanced experience.
+
+In plain words
+
+> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and logging.
+
+Microsoft documentation states
+
+> An ambassador service can be thought of as an out-of-process proxy which is co-located with the client. This pattern can be useful for offloading common client connectivity tasks such as monitoring, logging, routing, security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications, or other applications that are difficult to modify, in order to extend their networking capabilities. It can also enable a specialized team to implement those features.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Ambassador Pattern in Java
+
+In this example of the Ambassador Pattern in Java, we demonstrate how to implement latency checks, logging, and retry mechanisms to improve system reliability.
+
+A remote service has many clients accessing a function it provides. The service is a legacy application and is impossible to update. Large numbers of requests from users are causing connectivity issues. New rules for request frequency should be implemented along with latency checks and client-side logging.
+
+With the above introduction in mind we will imitate the functionality in this example. We have an interface implemented by the remote service as well as the ambassador service.
+
+```java
+interface RemoteServiceInterface {
+ long doRemoteFunction(int value) throws Exception;
+}
+```
+
+A remote services represented as a singleton.
+
+```java
+
+@Slf4j
+public class RemoteService implements RemoteServiceInterface {
+ private static RemoteService service = null;
+
+ static synchronized RemoteService getRemoteService() {
+ if (service == null) {
+ service = new RemoteService();
+ }
+ return service;
+ }
+
+ private RemoteService() {
+ }
+
+ @Override
+ public long doRemoteFunction(int value) {
+ long waitTime = (long) Math.floor(Math.random() * 1000);
+
+ try {
+ sleep(waitTime);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep interrupted", e);
+ }
+
+ return waitTime >= 200 ? value * 10 : -1;
+ }
+}
+```
+
+A service ambassador adds additional features such as logging and latency checks.
+
+```java
+
+@Slf4j
+public class ServiceAmbassador implements RemoteServiceInterface {
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
+
+ ServiceAmbassador() {
+ }
+
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
+
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken (ms): " + timeTaken);
+ return result;
+ }
+
+ private long safeCall(int value) {
+ var retries = 0;
+ var result = (long) FAILURE;
+
+ for (int i = 0; i < RETRIES; i++) {
+ if (retries >= RETRIES) {
+ return FAILURE;
+ }
+
+ if ((result = checkLatency(value)) == FAILURE) {
+ LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
+ retries++;
+ try {
+ sleep(DELAY_MS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ }
+ } else {
+ break;
+ }
+ }
+ return result;
+ }
+}
+```
+
+A client has a local service ambassador used to interact with the remote service.
+
+```java
+
+@Slf4j
+public class Client {
+ private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+
+ long useService(int value) {
+ var result = serviceAmbassador.doRemoteFunction(value);
+ LOGGER.info("Service result: " + result);
+ return result;
+ }
+}
+```
+
+Here are two clients using the service.
+
+```java
+public class App {
+ public static void main(String[] args) {
+ var host1 = new Client();
+ var host2 = new Client();
+ host1.useService(12);
+ host2.useService(73);
+ }
+}
+```
+
+Here's the output for running the example:
+
+```java
+Time taken(ms):111
+Service result:120
+Time taken(ms):931
+Failed to reach remote:(1)
+Time taken(ms):665
+Failed to reach remote:(2)
+Time taken(ms):538
+Failed to reach remote:(3)
+Service result:-1
+```
+
+## When to Use the Ambassador Pattern in Java
+
+* The Ambassador Pattern is particularly beneficial for Cloud Native and Microservices Architectures in Java. It helps in monitoring, logging, and securing inter-service communication, making it ideal for distributed systems.
+* Legacy System Integration: Facilitates communication with newer services by handling necessary but non-core functionalities.
+* Performance Enhancement: Can be used to cache results or compress data to improve communication efficiency.
+
+Typical use cases include:
+
+* Control access to another object
+* Implement logging
+* Implement circuit breaking
+* Offload remote service tasks
+* Facilitate network connection
+
+## Benefits and Trade-offs of Ambassador Pattern
+
+Benefits:
+
+* Separation of Concerns: Offloads cross-cutting concerns from the service logic, leading to cleaner, more maintainable code.
+* Reusable Infrastructure Logic: The ambassador pattern allows the same logic (e.g., logging, monitoring) to be reused across multiple services.
+* Improved Security: Centralizes security features like SSL termination or authentication, reducing the risk of misconfiguration.
+* Flexibility: Makes it easier to update or replace infrastructure concerns without modifying the service code.
+
+Trade-offs:
+
+* Increased Complexity: Adds another layer to the architecture, which can complicate the system design and debugging.
+* Potential Performance Overhead: The additional network hop can introduce latency and overhead, particularly if not optimized.
+* Deployment Overhead: Requires additional resources and management for deploying and scaling ambassador services.
+
+## Real-World Applications of Ambassador Pattern in Java
+
+* Service Mesh Implementations: In a service mesh architecture, like Istio or Linkerd, the Ambassador pattern is often employed as a sidecar proxy that handles inter-service communications. This includes tasks such as service discovery, routing, load balancing, telemetry (metrics and tracing), and security (authentication and authorization).
+* API Gateways: API gateways can use the Ambassador pattern to encapsulate common functionalities like rate limiting, caching, request shaping, and authentication. This allows backend services to focus on their core business logic without being burdened by these cross-cutting concerns.
+* Logging and Monitoring: An Ambassador can aggregate logs and metrics from various services and forward them to centralized monitoring tools like Prometheus or ELK Stack (Elasticsearch, Logstash, Kibana). This simplifies the logging and monitoring setup for each service and provides a unified view of the system's health.
+* Security: Security-related functionalities such as SSL/TLS termination, identity verification, and encryption can be managed by an Ambassador. This ensures consistent security practices across services and reduces the likelihood of security breaches due to misconfigurations.
+* Resilience: The Ambassador can implement resilience patterns like circuit breakers, retries, and timeouts. For instance, Netflix's Hystrix library can be used within an Ambassador to prevent cascading failures in a microservices ecosystem.
+* Database Proxy: Ambassadors can act as proxies for database connections, providing functionalities like connection pooling, read/write splitting for replicas, and query caching. This offloads significant complexity from the application services.
+* Legacy System Integration: In scenarios where modern microservices need to communicate with legacy systems, an Ambassador can serve as an intermediary that translates protocols, handles necessary transformations, and implements modern security practices, easing the integration process.
+* Network Optimization: For services deployed across different geographical locations or cloud regions, Ambassadors can optimize communication by compressing data, batching requests, or even implementing smart routing to reduce latency and costs.
+* [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador)
+
+## Related Java Design Patterns
+
+* [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/): Often used in conjunction to manage fault tolerance by stopping calls to an unresponsive service.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): The decorator pattern is used to add functionality to an object dynamically, while the ambassador pattern is used to offload functionality to a separate object.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): Shares similarities with the proxy pattern, but the ambassador pattern specifically focuses on offloading ancillary functionalities.
+* Sidecar: A similar pattern used in the context of containerized applications, where a sidecar container provides additional functionality to the main application container.
+
+## References and Credits
+
+* [Building Microservices: Designing Fine-Grained Systems](https://amzn.to/43aGpSR)
+* [Cloud Native Patterns: Designing Change-tolerant Software](https://amzn.to/3wUAl4O)
+* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://amzn.to/3T9g9Uj)
+* [Microservices Patterns: With examples in Java](https://amzn.to/3UyWD5O)
+* [Ambassador pattern (Microsoft)](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
diff --git a/ambassador/etc/ambassador-sequence-diagram.png b/ambassador/etc/ambassador-sequence-diagram.png
new file mode 100644
index 000000000000..71e6a947c5dd
Binary files /dev/null and b/ambassador/etc/ambassador-sequence-diagram.png differ
diff --git a/ambassador/etc/ambassador.urm.png b/ambassador/etc/ambassador.urm.png
new file mode 100644
index 000000000000..9b50a02ad356
Binary files /dev/null and b/ambassador/etc/ambassador.urm.png differ
diff --git a/ambassador/etc/ambassador.urm.puml b/ambassador/etc/ambassador.urm.puml
new file mode 100644
index 000000000000..517b0bf51d07
--- /dev/null
+++ b/ambassador/etc/ambassador.urm.puml
@@ -0,0 +1,47 @@
+@startuml
+package com.iluwatar.ambassador.util {
+ interface RandomProvider {
+ + random() : double {abstract}
+ }
+}
+package com.iluwatar.ambassador {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class Client {
+ - LOGGER : Logger {static}
+ - serviceAmbassador : ServiceAmbassador
+ + Client()
+ ~ useService(value : int) : long
+ }
+ class RemoteService {
+ - LOGGER : Logger {static}
+ - THRESHOLD : int {static}
+ - randomProvider : RandomProvider
+ - service : RemoteService {static}
+ - RemoteService()
+ ~ RemoteService(randomProvider : RandomProvider)
+ + doRemoteFunction(value : int) : long
+ ~ getRemoteService() : RemoteService {static}
+ }
+ ~interface RemoteServiceInterface {
+ + FAILURE : int {static}
+ + doRemoteFunction(int) : long {abstract}
+ }
+ class ServiceAmbassador {
+ - DELAY_MS : int {static}
+ - LOGGER : Logger {static}
+ - RETRIES : int {static}
+ ~ ServiceAmbassador()
+ - checkLatency(value : int) : long
+ + doRemoteFunction(value : int) : long
+ - safeCall(value : int) : long
+ }
+}
+RemoteService --> "-service" RemoteService
+Client --> "-serviceAmbassador" ServiceAmbassador
+RemoteService --> "-randomProvider" RandomProvider
+RemoteService ..|> RemoteServiceInterface
+ServiceAmbassador ..|> RemoteServiceInterface
+@enduml
\ No newline at end of file
diff --git a/ambassador/pom.xml b/ambassador/pom.xml
new file mode 100644
index 000000000000..15e4a07f0dc3
--- /dev/null
+++ b/ambassador/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ ambassador
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.ambassador.App
+
+
+
+
+
+
+
+
+
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/App.java b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
new file mode 100644
index 000000000000..8de149fe0813
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+/**
+ * The ambassador pattern creates a helper service that sends network requests on behalf of a
+ * client. It is often used in cloud-based applications to offload features of a remote service.
+ *
+ * An ambassador service can be thought of as an out-of-process proxy that is co-located with the
+ * client. Similar to the proxy design pattern, the ambassador service provides an interface for
+ * another remote service. In addition to the interface, the ambassador provides extra functionality
+ * and features, specifically offloaded common connectivity tasks. This usually consists of
+ * monitoring, logging, routing, security etc. This is extremely useful in legacy applications where
+ * the codebase is difficult to modify and allows for improvements in the application's networking
+ * capabilities.
+ *
+ *
In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
+ * the ({@link RemoteService}) class represents a remote application.
+ */
+public class App {
+
+ /** Entry point. */
+ public static void main(String[] args) {
+ var host1 = new Client();
+ var host2 = new Client();
+ host1.useService(12);
+ host2.useService(73);
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
new file mode 100644
index 000000000000..0baabf4ffc06
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** A simple Client. */
+@Slf4j
+public class Client {
+
+ private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+
+ long useService(int value) {
+ var result = serviceAmbassador.doRemoteFunction(value);
+ LOGGER.info("Service result: {}", result);
+ return result;
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
new file mode 100644
index 000000000000..d99348040cfe
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
@@ -0,0 +1,78 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import static java.lang.Thread.sleep;
+
+import com.iluwatar.ambassador.util.RandomProvider;
+import lombok.extern.slf4j.Slf4j;
+
+/** A remote legacy application represented by a Singleton implementation. */
+@Slf4j
+public class RemoteService implements RemoteServiceInterface {
+ private static final int THRESHOLD = 200;
+ private static RemoteService service = null;
+ private final RandomProvider randomProvider;
+
+ static synchronized RemoteService getRemoteService() {
+ if (service == null) {
+ service = new RemoteService();
+ }
+ return service;
+ }
+
+ private RemoteService() {
+ this(Math::random);
+ }
+
+ /** This constructor is used for testing purposes only. */
+ RemoteService(RandomProvider randomProvider) {
+ this.randomProvider = randomProvider;
+ }
+
+ /**
+ * Remote function takes a value and multiplies it by 10 taking a random amount of time. Will
+ * sometimes return -1. This imitates connectivity issues a client might have to account for.
+ *
+ * @param value integer value to be multiplied.
+ * @return if waitTime is less than {@link RemoteService#THRESHOLD}, it returns value * 10,
+ * otherwise {@link RemoteServiceStatus#FAILURE}.
+ */
+ @Override
+ public long doRemoteFunction(int value) {
+
+ long waitTime = (long) Math.floor(randomProvider.random() * 1000);
+
+ try {
+ sleep(waitTime);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ Thread.currentThread().interrupt();
+ }
+ return waitTime <= THRESHOLD
+ ? value * 10
+ : RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
new file mode 100644
index 000000000000..aa6012bae33f
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+/** Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */
+interface RemoteServiceInterface {
+
+ long doRemoteFunction(int value);
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
new file mode 100644
index 000000000000..8549ed7247f3
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import lombok.Getter;
+
+/**
+ * Holds information regarding the status of the Remote Service.
+ *
+ *
This Enum replaces the integer value previously stored in {@link RemoteServiceInterface} as
+ * SonarCloud was identifying it as an issue. All test cases have been checked after changes,
+ * without failures.
+ */
+public enum RemoteServiceStatus {
+ FAILURE(-1);
+
+ @Getter private final long remoteServiceStatusValue;
+
+ RemoteServiceStatus(long remoteServiceStatusValue) {
+ this.remoteServiceStatusValue = remoteServiceStatusValue;
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
new file mode 100644
index 000000000000..4d310169770b
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
@@ -0,0 +1,83 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import static com.iluwatar.ambassador.RemoteServiceStatus.FAILURE;
+import static java.lang.Thread.sleep;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ServiceAmbassador provides an interface for a ({@link Client}) to access ({@link RemoteService}).
+ * The interface adds logging, latency testing and usage of the service in a safe way that will not
+ * add stress to the remote service when connectivity issues occur.
+ */
+@Slf4j
+public class ServiceAmbassador implements RemoteServiceInterface {
+
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
+
+ ServiceAmbassador() {}
+
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
+
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken (ms): {}", timeTaken);
+ return result;
+ }
+
+ private long safeCall(int value) {
+ var retries = 0;
+ var result = FAILURE.getRemoteServiceStatusValue();
+
+ for (int i = 0; i < RETRIES; i++) {
+ if (retries >= RETRIES) {
+ return FAILURE.getRemoteServiceStatusValue();
+ }
+
+ if ((result = checkLatency(value)) == FAILURE.getRemoteServiceStatusValue()) {
+ LOGGER.info("Failed to reach remote: ({})", i + 1);
+ retries++;
+ try {
+ sleep(DELAY_MS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ Thread.currentThread().interrupt();
+ }
+ } else {
+ break;
+ }
+ }
+ return result;
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
new file mode 100644
index 000000000000..4eba2fada7fa
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador.util;
+
+/** An interface for randomness. Useful for testing purposes. */
+public interface RandomProvider {
+ double random();
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
new file mode 100644
index 000000000000..ddb2d6eff411
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application test */
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
new file mode 100644
index 000000000000..24603efff9d4
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
@@ -0,0 +1,42 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link Client} */
+class ClientTest {
+
+ @Test
+ void test() {
+ Client client = new Client();
+ var result = client.useService(10);
+
+ assertTrue(
+ result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ }
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
new file mode 100644
index 000000000000..81e4f744128f
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
@@ -0,0 +1,61 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.iluwatar.ambassador.util.RandomProvider;
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link RemoteService} */
+class RemoteServiceTest {
+
+ @Test
+ void testFailedCall() {
+ var remoteService = new RemoteService(new StaticRandomProvider(0.21));
+ var result = remoteService.doRemoteFunction(10);
+ assertEquals(RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue(), result);
+ }
+
+ @Test
+ void testSuccessfulCall() {
+ var remoteService = new RemoteService(new StaticRandomProvider(0.2));
+ var result = remoteService.doRemoteFunction(10);
+ assertEquals(100, result);
+ }
+
+ private static class StaticRandomProvider implements RandomProvider {
+ private final double value;
+
+ StaticRandomProvider(double value) {
+ this.value = value;
+ }
+
+ @Override
+ public double random() {
+ return value;
+ }
+ }
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
new file mode 100644
index 000000000000..0543b2e7e370
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link ServiceAmbassador} */
+class ServiceAmbassadorTest {
+
+ @Test
+ void test() {
+ long result = new ServiceAmbassador().doRemoteFunction(10);
+ assertTrue(
+ result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ }
+}
diff --git a/anti-corruption-layer/README.md b/anti-corruption-layer/README.md
new file mode 100644
index 000000000000..9b6042850eba
--- /dev/null
+++ b/anti-corruption-layer/README.md
@@ -0,0 +1,185 @@
+---
+title: "Anti-Corruption Layer Pattern in Java: Ensuring System Integrity Amidst Legacy Systems"
+shortTitle: Anti-Corruption Layer
+description: "Learn how the Anti-Corruption Layer design pattern helps in decoupling subsystems, preventing data corruption, and facilitating seamless integration in Java applications."
+category: Integration
+language: en
+tag:
+ - Architecture
+ - Decoupling
+ - Integration
+ - Isolation
+ - Layered architecture
+ - Migration
+ - Modernization
+ - Refactoring
+ - Wrapping
+---
+
+## Also known as
+
+* ACL
+* Interface layer
+* Translation layer
+
+## Intent of Anti-Corruption Layer Design Pattern
+
+The Anti-Corruption Layer (ACL) is a crucial design pattern in Java development, particularly for system integration and maintaining data integrity. Implement a façade or adapter layer between different subsystems that don't share the same semantics. It translates between different data formats and systems, ensuring that the integration between systems does not lead to corruption of business logic or data integrity.
+
+## Detailed Explanation of Anti-Corruption Layer Pattern with Real-World Examples
+
+Real-world example
+
+> This example demonstrates how the Anti-Corruption Layer ensures seamless integration between legacy systems and modern platforms, crucial for maintaining business logic integrity during system migration.
+>
+> Imagine a large retail company transitioning its inventory management system from an old legacy software to a new modern platform. The legacy system has been in use for decades and contains complex business rules and data formats that are incompatible with the new system. Instead of directly connecting the new system to the legacy one, the company implements an Anti-Corruption Layer (ACL).
+>
+> The ACL acts as a mediator, translating and adapting data between the two systems. When the new system requests inventory data, the ACL translates the request into a format the legacy system understands, retrieves the data, and then translates it back into a format suitable for the new system. This approach ensures that the new system remains unaffected by the intricacies of the legacy system, preventing corruption of data and business logic while facilitating a smooth transition.
+
+In plain words
+
+> The Anti-Corruption Layer design pattern protects a system from the complexities and changes of external systems by providing an intermediary translation layer.
+
+[Microsoft's documentation](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer) says
+
+> Implement a façade or adapter layer between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to the other subsystem. Use this pattern to ensure that an application's design is not limited by dependencies on outside subsystems. This pattern was first described by Eric Evans in Domain-Driven Design.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Anti-Corruption Layer Pattern in Java
+
+The ACL design pattern in Java provides an intermediary layer that translates data formats, ensuring that integration between different systems does not lead to data corruption.
+
+Here are 2 shop-ordering systems: `Legacy` and `Modern`.
+
+The aforementioned systems have different domain models and have to operate simultaneously. Since they work independently the orders can come either from the `Legacy` or `Modern` system. Therefore, the system that receives the legacyOrder needs to check if the legacyOrder is valid and not present in the other system. Then it can place the legacyOrder in its own system.
+
+But for that, the system needs to know the domain model of the other system and to avoid that, the anti-corruption layer(ACL) is introduced. The ACL is a layer that translates the domain model of the `Legacy` system to the domain model of the `Modern` system and vice versa. Also, it hides all other operations with the other system, uncoupling the systems.
+
+Domain model of the `Legacy` system:
+
+```java
+public class LegacyOrder {
+ private String id;
+ private String customer;
+ private String item;
+ private String qty;
+ private String price;
+}
+```
+
+Domain model of the `Modern` system:
+
+```java
+public class ModernOrder {
+ private String id;
+ private Customer customer;
+
+ private Shipment shipment;
+
+ private String extra;
+}
+
+public class Customer {
+ private String address;
+}
+
+public class Shipment {
+ private String item;
+ private String qty;
+ private String price;
+}
+```
+
+Anti-corruption layer:
+
+```java
+public class AntiCorruptionLayer {
+
+ @Autowired
+ private ModernShop modernShop;
+
+ @Autowired
+ private LegacyShop legacyShop;
+
+ public Optional findOrderInModernSystem(String id) {
+ return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/);
+ }
+
+ public Optional findOrderInLegacySystem(String id) {
+ return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/);
+ }
+
+}
+```
+
+The connection between the systems. Wherever the `Legacy` or `Modern` system needs to communicate with the counterpart the ACL needs to be used to avoid corrupting the current domain model. The example below shows how the `Legacy` system places an order with a validation from the `Modern` system.
+
+```java
+public class LegacyShop {
+ @Autowired
+ private AntiCorruptionLayer acl;
+
+ public void placeOrder(LegacyOrder legacyOrder) throws ShopException {
+
+ String id = legacyOrder.getId();
+
+ Optional orderInModernSystem = acl.findOrderInModernSystem(id);
+
+ if (orderInModernSystem.isPresent()) {
+ // order is already in the modern system
+ } else {
+ // place order in the current system
+ }
+ }
+}
+```
+
+## When to Use the Anti-Corruption Layer Pattern in Java
+
+Use this pattern when:
+
+* A migration is planned to happen over multiple stages, but integration between new and legacy systems needs to be maintained
+* Two or more subsystems have different semantics, but still need to communicate
+* When integrating with legacy systems or external systems where direct integration might pollute the domain model of the new system
+* In scenarios where different subsystems within a larger system use different data formats or structures
+* When there is a need to ensure loose coupling between different subsystems or external services to facilitate easier maintenance and scalability
+
+## Anti-Corruption Layer Pattern Java Tutorials
+
+* [Anti-Corruption Layer (Microsoft)](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer)
+* [Anti-Corruption Layer Pattern (Amazon)](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html)
+
+## Real-World Applications of Anti-Corruption Layer Pattern in Java
+
+* Microservices architectures where individual services must communicate without being tightly coupled to each other’s data schemas
+* Enterprise systems integration, especially when integrating modern systems with legacy systems
+* In bounded contexts within Domain-Driven Design (DDD) to maintain the integrity of a domain model when interacting with external systems or subsystems
+
+## Benefits and Trade-offs of Anti-Corruption Layer Pattern
+
+Benefits:
+
+* Protects the integrity of the domain model by providing a clear boundary
+* Promotes loose coupling between systems, making the system more resilient to changes in external systems
+* Facilitates cleaner and more maintainable code by isolating integration code from business logic
+
+Trade-offs:
+
+* Introduces additional complexity and potential performance overhead due to the translation process
+* Requires extra effort in design and implementation to ensure the layer is effective without becoming a bottleneck
+* Can lead to duplication of models if not carefully managed
+
+## Related Java Design Patterns
+
+* [Adapter](https://java-design-patterns.com/patterns/adapter/): The Anti-Corruption Layer can be implemented using the Adapter pattern to translate between different data formats or structures
+* [Facade](https://java-design-patterns.com/patterns/facade/): The Anti-Corruption Layer can be seen as a specialized form of the Facade pattern that is used to isolate different subsystems
+* [Gateway](https://java-design-patterns.com/patterns/gateway/): The Anti-Corruption Layer can be used as a Gateway to external systems to provide a unified interface
+
+## References and Credits
+
+* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3vptcJz)
+* [Implementing Domain-Driven Design](https://amzn.to/3ISOSRA)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
diff --git a/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png b/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png
new file mode 100644
index 000000000000..835ff4e84a0f
Binary files /dev/null and b/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png differ
diff --git a/anti-corruption-layer/etc/anti-corruption-layer.urm.png b/anti-corruption-layer/etc/anti-corruption-layer.urm.png
new file mode 100644
index 000000000000..96e432121e7e
Binary files /dev/null and b/anti-corruption-layer/etc/anti-corruption-layer.urm.png differ
diff --git a/anti-corruption-layer/etc/anti-corruption-layer.urm.puml b/anti-corruption-layer/etc/anti-corruption-layer.urm.puml
new file mode 100644
index 000000000000..6b1d1cdb9b81
--- /dev/null
+++ b/anti-corruption-layer/etc/anti-corruption-layer.urm.puml
@@ -0,0 +1,25 @@
+@startuml
+package com.iluwatar.corruption {
+class LegacyShop {
+ private Store store;
+ private AntiCorruptionLayer acl;
+}
+
+class ModernShop {
+ private Store store;
+ private AntiCorruptionLayer acl;
+}
+
+class AntiCorruptionLayer{
+ private LegacyShop legacyShop;
+ private ModernShop modernShop;
+
+
+ }
+LegacyShop ---> "findOrderInModernSystem" AntiCorruptionLayer
+ModernShop ---> "findOrderInLegacySystem" AntiCorruptionLayer
+AntiCorruptionLayer ..|> ModernShop
+AntiCorruptionLayer ..|> LegacyShop
+}
+
+@enduml
\ No newline at end of file
diff --git a/anti-corruption-layer/pom.xml b/anti-corruption-layer/pom.xml
new file mode 100644
index 000000000000..2fddfd3ecc46
--- /dev/null
+++ b/anti-corruption-layer/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ anti-corruption-layer
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.corruption.App
+
+
+
+
+
+
+
+
+
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
new file mode 100644
index 000000000000..f7cf8f075f83
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * This layer translates communications between the two systems, allowing one system to remain
+ * unchanged while the other can avoid compromising its design and technological approach.
+ */
+@SpringBootApplication
+public class App {
+
+ public static void main(String[] args) {
+ SpringApplication.run(App.class, args);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
new file mode 100644
index 000000000000..880d98c7d5b0
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
@@ -0,0 +1,48 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.
+ */
+/**
+ * Context and problem Most applications rely on other systems for some data or functionality. For
+ * example, when a legacy application is migrated to a modern system, it may still need existing
+ * legacy resources. New features must be able to call the legacy system. This is especially true of
+ * gradual migrations, where different features of a larger application are moved to a modern system
+ * over time.
+ *
+ * Often these legacy systems suffer from quality issues such as convoluted data schemas or
+ * obsolete APIs. The features and technologies used in legacy systems can vary widely from more
+ * modern systems. To interoperate with the legacy system, the new application may need to support
+ * outdated infrastructure, protocols, data models, APIs, or other features that you wouldn't
+ * otherwise put into a modern application.
+ *
+ *
Maintaining access between new and legacy systems can force the new system to adhere to at
+ * least some of the legacy system's APIs or other semantics. When these legacy features have
+ * quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern
+ * application. Similar issues can arise with any external system that your development team doesn't
+ * control, not just legacy systems.
+ *
+ *
Solution Isolate the different subsystems by placing an anti-corruption layer between them.
+ * This layer translates communications between the two systems, allowing one system to remain
+ * unchanged while the other can avoid compromising its design and technological approach.
+ */
+package com.iluwatar.corruption;
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
new file mode 100644
index 000000000000..4e8a17fa5d2a
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
@@ -0,0 +1,66 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system;
+
+import com.iluwatar.corruption.system.legacy.LegacyShop;
+import com.iluwatar.corruption.system.modern.Customer;
+import com.iluwatar.corruption.system.modern.ModernOrder;
+import com.iluwatar.corruption.system.modern.Shipment;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents an anti-corruption layer. The main purpose of the class is to provide a
+ * layer between the modern and legacy systems. The class is responsible for converting the data
+ * from one system to another decoupling the systems to each other
+ *
+ *
It allows using one system a domain model of the other system without changing the domain
+ * model of the system.
+ */
+@Service
+public class AntiCorruptionLayer {
+
+ @Autowired private LegacyShop legacyShop;
+
+ /**
+ * The method converts the order from the legacy system to the modern system.
+ *
+ * @param id the id of the order
+ * @return the order in the modern system
+ */
+ public Optional findOrderInLegacySystem(String id) {
+
+ return legacyShop
+ .findOrder(id)
+ .map(
+ o ->
+ new ModernOrder(
+ o.getId(),
+ new Customer(o.getCustomer()),
+ new Shipment(o.getItem(), o.getQty(), o.getPrice()),
+ ""));
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
new file mode 100644
index 000000000000..e84578528be7
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
@@ -0,0 +1,49 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+/**
+ * The class represents a data store for the modern system.
+ *
+ * @param the type of the value stored in the data store
+ */
+public abstract class DataStore {
+ private final HashMap inner;
+
+ public DataStore() {
+ inner = new HashMap<>();
+ }
+
+ public Optional get(String key) {
+ return Optional.ofNullable(inner.get(key));
+ }
+
+ public Optional put(String key, V value) {
+ return Optional.ofNullable(inner.put(key, value));
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
new file mode 100644
index 000000000000..c0acd288ed0c
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
@@ -0,0 +1,50 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system;
+
+/** The class represents a general exception for the shop. */
+public class ShopException extends Exception {
+ public ShopException(String message) {
+ super(message);
+ }
+
+ /**
+ * Throws an exception that the order is already placed but has an incorrect data.
+ *
+ * @param lhs the incoming order
+ * @param rhs the existing order
+ * @return the exception
+ * @throws ShopException the exception
+ */
+ public static ShopException throwIncorrectData(String lhs, String rhs) throws ShopException {
+ throw new ShopException(
+ "The order is already placed but has an incorrect data:\n"
+ + "Incoming order: "
+ + lhs
+ + "\n"
+ + "Existing order: "
+ + rhs);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
new file mode 100644
index 000000000000..45faa06cb26c
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
@@ -0,0 +1,43 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.legacy;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The class represents an order in the legacy system. The class is used by the legacy system to
+ * store the data.
+ */
+@Data
+@AllArgsConstructor
+public class LegacyOrder {
+ private String id;
+ private String customer;
+
+ private String item;
+ private int qty;
+ private int price;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java
new file mode 100644
index 000000000000..b74eb1c29718
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java
@@ -0,0 +1,52 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.legacy;
+
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a legacy shop system. The main purpose is to place the order from the
+ * customers.
+ */
+@Service
+public class LegacyShop {
+ @Autowired private LegacyStore store;
+
+ /**
+ * Places the order in the legacy system. If the order is already present in the modern system,
+ * then the order is placed only if the data is the same. If the order is not present in the
+ * modern system, then the order is placed in the legacy system.
+ */
+ public void placeOrder(LegacyOrder legacyOrder) {
+ store.put(legacyOrder.getId(), legacyOrder);
+ }
+
+ /** Finds the order in the legacy system. */
+ public Optional findOrder(String orderId) {
+ return store.get(orderId);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
new file mode 100644
index 000000000000..ec1d613a7235
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.legacy;
+
+import com.iluwatar.corruption.system.DataStore;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a data store for the legacy system. The class is used by the legacy system
+ * to store the data.
+ */
+@Service
+public class LegacyStore extends DataStore {}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
new file mode 100644
index 000000000000..130f36d39674
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The class represents a customer in the modern system. */
+@Data
+@AllArgsConstructor
+public class Customer {
+ private String address;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
new file mode 100644
index 000000000000..7b62985015d6
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The class represents an order in the modern system. */
+@Data
+@AllArgsConstructor
+public class ModernOrder {
+ private String id;
+ private Customer customer;
+
+ private Shipment shipment;
+
+ private String extra;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
new file mode 100644
index 000000000000..24080abe1533
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
@@ -0,0 +1,67 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.modern;
+
+import com.iluwatar.corruption.system.AntiCorruptionLayer;
+import com.iluwatar.corruption.system.ShopException;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a modern shop system. The main purpose of the class is to place orders and
+ * find orders.
+ */
+@Service
+public class ModernShop {
+ @Autowired private ModernStore store;
+
+ @Autowired private AntiCorruptionLayer acl;
+
+ /**
+ * Places the order in the modern system. If the order is already present in the legacy system,
+ * then no need to place it again.
+ */
+ public void placeOrder(ModernOrder order) throws ShopException {
+
+ String id = order.getId();
+ // check if the order is already present in the legacy system
+ Optional orderInObsoleteSystem = acl.findOrderInLegacySystem(id);
+
+ if (orderInObsoleteSystem.isPresent()) {
+ var legacyOrder = orderInObsoleteSystem.get();
+ if (!order.equals(legacyOrder)) {
+ throw ShopException.throwIncorrectData(legacyOrder.toString(), order.toString());
+ }
+ } else {
+ store.put(id, order);
+ }
+ }
+
+ /** Finds the order in the modern system. */
+ public Optional findOrder(String orderId) {
+ return store.get(orderId);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
new file mode 100644
index 000000000000..4fb3952fae5e
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
@@ -0,0 +1,32 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.modern;
+
+import com.iluwatar.corruption.system.DataStore;
+import org.springframework.stereotype.Service;
+
+/** The class represents a data store for the modern system. */
+@Service
+public class ModernStore extends DataStore {}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
new file mode 100644
index 000000000000..085a3921ceeb
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The class represents a shipment in the modern system. The class is used by the modern system to
+ * store the data.
+ */
+@Data
+@AllArgsConstructor
+public class Shipment {
+ private String item;
+ private int qty;
+ private int price;
+}
diff --git a/anti-corruption-layer/src/main/resources/application.properties b/anti-corruption-layer/src/main/resources/application.properties
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
new file mode 100644
index 000000000000..ee46d124eee6
--- /dev/null
+++ b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
@@ -0,0 +1,96 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.corruption.system;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.corruption.system.legacy.LegacyOrder;
+import com.iluwatar.corruption.system.legacy.LegacyShop;
+import com.iluwatar.corruption.system.modern.Customer;
+import com.iluwatar.corruption.system.modern.ModernOrder;
+import com.iluwatar.corruption.system.modern.ModernShop;
+import com.iluwatar.corruption.system.modern.Shipment;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest
+public class AntiCorruptionLayerTest {
+
+ @Autowired private LegacyShop legacyShop;
+
+ @Autowired private ModernShop modernShop;
+
+ /**
+ * Test the anti-corruption layer. Main intention is to demonstrate how the anti-corruption layer
+ * works. The 2 shops (modern and legacy) should operate independently and in the same time
+ * synchronize the data.
+ */
+ @Test
+ public void antiCorruptionLayerTest() throws ShopException {
+ // a new order comes to the legacy shop.
+ LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
+ // place the order in the legacy shop.
+ legacyShop.placeOrder(legacyOrder);
+ // the order is placed as usual since there is no other orders with the id in the both systems.
+ Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
+ assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
+
+ // a new order (or maybe just the same order) appears in the modern shop
+ ModernOrder modernOrder =
+ new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), "");
+ // the system places it, but it checks if there is an order with the same id in the legacy shop.
+ modernShop.placeOrder(modernOrder);
+
+ Optional modernOrderWithIdOne = modernShop.findOrder("1");
+ // there is no new order placed since there is already an order with the same id in the legacy
+ // shop.
+ assertTrue(modernOrderWithIdOne.isEmpty());
+ }
+
+ /**
+ * Test the anti-corruption layer when a conflict occurs between systems. This test ensures that
+ * an exception is thrown when conflicting orders are placed.
+ */
+ @Test
+ public void antiCorruptionLayerWithExTest() throws ShopException {
+ // a new order comes to the legacy shop.
+ LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
+ // place the order in the legacy shop.
+ legacyShop.placeOrder(legacyOrder);
+ // the order is placed as usual since there is no other orders with the id in the both systems.
+ Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
+ assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
+ // a new order but with the same id and different data appears in the modern shop
+ ModernOrder modernOrder =
+ new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), "");
+ // the system rejects the order since there are 2 orders with contradiction there.
+ assertThrows(ShopException.class, () -> modernShop.placeOrder(modernOrder));
+ }
+}
diff --git a/api-gateway/README.md b/api-gateway/README.md
deleted file mode 100644
index 93b975e131d3..000000000000
--- a/api-gateway/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-layout: pattern
-title: API Gateway
-folder: api-gateway
-permalink: /patterns/api-gateway/
-pumlid: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0
-categories: Architectural
-tags:
-- Java
-- Difficulty-Intermediate
-- Spring
----
-
-## Intent
-
-Aggregate calls to microservices in a single location: the API Gateway. The user makes a single
-call to the API Gateway, and the API Gateway then calls each relevant microservice.
-
-
-
-## Applicability
-
-Use the API Gateway pattern when
-
-* you're also using the Microservices pattern and need a single point of aggregation for your
-microservice calls
-
-## Credits
-
-* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html)
-* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/)
diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml
deleted file mode 100644
index a97c25cfc3c4..000000000000
--- a/api-gateway/api-gateway-service/pom.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- 4.0.0
- api-gateway-service
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
-
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- junit
- junit
- test
-
-
- org.mockito
- mockito-core
- test
-
-
- org.apache.httpcomponents
- httpclient
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- ${spring-boot.version}
-
-
-
- repackage
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
deleted file mode 100644
index aab1247963a7..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.api.gateway;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.annotation.Resource;
-
-/**
- * The ApiGateway aggregates calls to microservices based on the needs of the individual clients.
- */
-@RestController
-public class ApiGateway {
-
- @Resource
- private ImageClient imageClient;
-
- @Resource
- private PriceClient priceClient;
-
- /**
- * Retrieves product information that desktop clients need
- * @return Product information for clients on a desktop
- */
- @RequestMapping("/desktop")
- public DesktopProduct getProductDesktop() {
- DesktopProduct desktopProduct = new DesktopProduct();
- desktopProduct.setImagePath(imageClient.getImagePath());
- desktopProduct.setPrice(priceClient.getPrice());
- return desktopProduct;
- }
-
- /**
- * Retrieves product information that mobile clients need
- * @return Product information for clients on a mobile device
- */
- @RequestMapping("/mobile")
- public MobileProduct getProductMobile() {
- MobileProduct mobileProduct = new MobileProduct();
- mobileProduct.setPrice(priceClient.getPrice());
- return mobileProduct;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java
deleted file mode 100644
index 3b529aa5fe96..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.api.gateway;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * With the Microservices pattern, a client may need data from multiple different microservices.
- * If the client called each microservice directly, that could contribute to longer load times,
- * since the client would have to make a network request for each microservice called. Moreover,
- * having the client call each microservice directly ties the client to that microservice - if the
- * internal implementations of the microservices change (for example, if two microservices are
- * combined sometime in the future) or if the location (host and port) of a microservice changes,
- * then every client that makes use of those microservices must be updated.
- *
- *
- * The intent of the API Gateway pattern is to alleviate some of these issues. In the API Gateway
- * pattern, an additional entity (the API Gateway) is placed between the client and the
- * microservices. The job of the API Gateway is to aggregate the calls to the microservices.
- * Rather than the client calling each microservice individually, the client calls the API Gateway
- * a single time. The API Gateway then calls each of the microservices that the client needs.
- *
- *
- * This implementation shows what the API Gateway pattern could look like for an e-commerce site.
- * The {@link ApiGateway} makes calls to the Image and Price microservices using the
- * {@link ImageClientImpl} and {@link PriceClientImpl} respectively. Customers viewing the site on a
- * desktop device can see both price information and an image of a product, so the {@link ApiGateway}
- * calls both of the microservices and aggregates the data in the {@link DesktopProduct} model.
- * However, mobile users only see price information; they do not see a product image. For mobile
- * users, the {@link ApiGateway} only retrieves price information, which it uses to populate the
- * {@link MobileProduct}.
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point
- *
- * @param args
- * command line args
- */
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java
deleted file mode 100644
index e72b98e73c40..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.api.gateway;
-
-/**
- * Encapsulates all of the information that a desktop client needs to display a product.
- */
-public class DesktopProduct {
- /**
- * The price of the product
- */
- private String price;
-
- /**
- * The path to the image of the product
- */
- private String imagePath;
-
- public String getPrice() {
- return price;
- }
-
- public void setPrice(String price) {
- this.price = price;
- }
-
- public String getImagePath() {
- return imagePath;
- }
-
- public void setImagePath(String imagePath) {
- this.imagePath = imagePath;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java
deleted file mode 100644
index ebabfe839b69..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.api.gateway;
-
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-import org.springframework.stereotype.Component;
-
-import java.io.IOException;
-
-/**
- * An adapter to communicate with the Image microservice
- */
-@Component
-public class ImageClientImpl implements ImageClient {
- /**
- * Makes a simple HTTP Get request to the Image microservice
- * @return The path to the image
- */
- @Override
- public String getImagePath() {
- String response = null;
- try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
- HttpGet httpGet = new HttpGet("http://localhost:50005/image-path");
- try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
- response = EntityUtils.toString(httpResponse.getEntity());
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return response;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java
deleted file mode 100644
index 87f44761c649..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.api.gateway;
-
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-import org.springframework.stereotype.Component;
-
-import java.io.IOException;
-
-/**
- * An adapter to communicate with the Price microservice
- */
-@Component
-public class PriceClientImpl implements PriceClient {
- /**
- * Makes a simple HTTP Get request to the Price microservice
- * @return The price of the product
- */
- @Override
- public String getPrice() {
- String response = null;
- try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
- HttpGet httpGet = new HttpGet("http://localhost:50006/price");
- try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
- response = EntityUtils.toString(httpResponse.getEntity());
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return response;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/resources/application.properties b/api-gateway/api-gateway-service/src/main/resources/application.properties
deleted file mode 100644
index ae91c5eb5625..000000000000
--- a/api-gateway/api-gateway-service/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright (c) 2014-2016 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.
-#
-
-server.port=50004
\ No newline at end of file
diff --git a/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java
deleted file mode 100644
index 6a79e5d00279..000000000000
--- a/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.api.gateway;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Test API Gateway Pattern
- */
-public class ApiGatewayTest {
-
- @InjectMocks
- private ApiGateway apiGateway;
-
- @Mock
- private ImageClient imageClient;
-
- @Mock
- private PriceClient priceClient;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- /**
- * Tests getting the data for a desktop client
- */
- @Test
- public void testGetProductDesktop() {
- String imagePath = "/product-image.png";
- String price = "20";
- when(imageClient.getImagePath()).thenReturn(imagePath);
- when(priceClient.getPrice()).thenReturn(price);
-
- DesktopProduct desktopProduct = apiGateway.getProductDesktop();
-
- assertEquals(price, desktopProduct.getPrice());
- assertEquals(imagePath, desktopProduct.getImagePath());
- }
-
- /**
- * Tests getting the data for a mobile client
- */
- @Test
- public void testGetProductMobile() {
- String price = "20";
- when(priceClient.getPrice()).thenReturn(price);
-
- MobileProduct mobileProduct = apiGateway.getProductMobile();
-
- assertEquals(price, mobileProduct.getPrice());
- }
-}
diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml
deleted file mode 100644
index 574623b3aea2..000000000000
--- a/api-gateway/image-microservice/pom.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.18.0-SNAPSHOT
-
-
- 4.0.0
- image-microservice
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
-
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- junit
- junit
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- ${spring-boot.version}
-
-
-
- repackage
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java
deleted file mode 100644
index 18d0d400806c..000000000000
--- a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.image.microservice;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * Exposes the Image microservice's endpoints
- */
-@RestController
-public class ImageController {
-
- /**
- * An endpoint for a user to retrieve an image path
- * @return An image path
- */
- @RequestMapping(value = "/image-path", method = RequestMethod.GET)
- public String getImagePath() {
- return "/product-image.png";
- }
-}
diff --git a/api-gateway/image-microservice/src/main/resources/application.properties b/api-gateway/image-microservice/src/main/resources/application.properties
deleted file mode 100644
index 5830a3c95c55..000000000000
--- a/api-gateway/image-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright (c) 2014-2016 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.
-#
-
-server.port=50005
\ No newline at end of file
diff --git a/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java b/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java
deleted file mode 100644
index 1b934014a415..000000000000
--- a/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.image.microservice;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Test for Image Rest Controller
- */
-public class ImageControllerTest {
- @Test
- public void testGetImagePath() {
- ImageController imageController = new ImageController();
-
- String imagePath = imageController.getImagePath();
-
- Assert.assertEquals("/product-image.png", imagePath);
- }
-}
diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml
deleted file mode 100644
index 124b3cf24d4e..000000000000
--- a/api-gateway/pom.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.18.0-SNAPSHOT
-
- 4.0.0
- api-gateway
- pom
-
-
-
-
- com.github.markusmo3.urm
- urm-maven-plugin
- ${urm.version}
-
- ${project.basedir}/../etc
-
- com.iluwatar
-
-
-
- api-gateway
-
-
-
-
-
-
-
- image-microservice
- price-microservice
- api-gateway-service
-
-
diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml
deleted file mode 100644
index aed9fb38b98d..000000000000
--- a/api-gateway/price-microservice/pom.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.18.0-SNAPSHOT
-
-
- 4.0.0
- price-microservice
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
-
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- junit
- junit
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- ${spring-boot.version}
-
-
-
- repackage
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java
deleted file mode 100644
index 906b55c88d14..000000000000
--- a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.price.microservice;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * Exposes the Price microservice's endpoints
- */
-@RestController
-public class PriceController {
-
- /**
- * An endpoint for a user to retrieve a product's price
- * @return A product's price
- */
- @RequestMapping(value = "/price", method = RequestMethod.GET)
- public String getPrice() {
- return "20";
- }
-}
diff --git a/api-gateway/price-microservice/src/main/resources/application.properties b/api-gateway/price-microservice/src/main/resources/application.properties
deleted file mode 100644
index d7b443b268e7..000000000000
--- a/api-gateway/price-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright (c) 2014-2016 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.
-#
-
-server.port=50006
\ No newline at end of file
diff --git a/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java b/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java
deleted file mode 100644
index b1fe66d42f76..000000000000
--- a/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * The MIT License
- * Copyright (c) 2014-2016 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.price.microservice;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-
-/**
- * Test for Price Rest Controller
- */
-public class PriceControllerTest {
- @Test
- public void testgetPrice() {
- PriceController priceController = new PriceController();
-
- String price = priceController.getPrice();
-
- Assert.assertEquals("20", price);
- }
-}
diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md
new file mode 100644
index 000000000000..584d8601a23e
--- /dev/null
+++ b/arrange-act-assert/README.md
@@ -0,0 +1,167 @@
+---
+title: "Arrange/Act/Assert Pattern in Java: Enhance Testing Clarity and Simplicity"
+shortTitle: Arrange/Act/Assert
+description: "Learn how to use the Arrange/Act/Assert pattern to structure your unit tests in Java. Improve readability and maintainability of your code with clear testing phases."
+category: Testing
+language: en
+tag:
+ - Code simplification
+ - Isolation
+ - Testing
+---
+
+## Also known as
+
+* Given/When/Then
+
+## Intent of Arrange/Act/Assert Design Pattern
+
+The Arrange/Act/Assert pattern is essential in unit testing in Java. This testing method structures unit tests clearly by dividing them into three distinct sections: setup (Arrange), execution (Act), and verification (Assert).
+
+## Detailed Explanation of Arrange/Act/Assert Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine you are organizing a small event. To ensure everything runs smoothly, you follow a pattern similar to Arrange/Act/Assert:
+>
+> 1. **Arrange**: You set up the venue, prepare the guest list, arrange seating, and organize the catering.
+> 2. **Act**: You conduct the event according to the plan, welcoming guests, serving food, and following the schedule.
+> 3. **Assert**: After the event, you evaluate its success by checking guest feedback, ensuring all tasks were completed, and reviewing if everything went as planned.
+>
+> This clear separation of preparation, execution, and evaluation helps ensure the event is well-organized and successful, mirroring the structured approach of the Arrange/Act/Assert pattern in software testing.
+
+In plain words
+
+> Arrange/Act/Assert is a testing pattern that organizes tests into three clear steps for easy maintenance.
+
+WikiWikiWeb says
+
+> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
+
+Flowchart
+
+
+
+## Programmatic Example of Arrange/Act/Assert Pattern in Java
+
+We need to write comprehensive and clear unit test suite for a class. Using the Arrange/Act/Assert pattern in Java testing ensures clarity.
+
+Let's first introduce our `Cash` class to be unit tested.
+
+```java
+public class Cash {
+
+ private int amount;
+
+ Cash(int amount) {
+ this.amount = amount;
+ }
+
+ void plus(int addend) {
+ amount += addend;
+ }
+
+ boolean minus(int subtrahend) {
+ if (amount >= subtrahend) {
+ amount -= subtrahend;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ int count() {
+ return amount;
+ }
+}
+```
+
+Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the clearly separated steps for each unit test.
+
+```java
+class CashAAATest {
+
+ @Test
+ void testPlus() {
+ //Arrange
+ var cash = new Cash(3);
+ //Act
+ cash.plus(4);
+ //Assert
+ assertEquals(7, cash.count());
+ }
+
+ @Test
+ void testMinus() {
+ //Arrange
+ var cash = new Cash(8);
+ //Act
+ var result = cash.minus(5);
+ //Assert
+ assertTrue(result);
+ assertEquals(3, cash.count());
+ }
+
+ @Test
+ void testInsufficientMinus() {
+ //Arrange
+ var cash = new Cash(1);
+ //Act
+ var result = cash.minus(6);
+ //Assert
+ assertFalse(result);
+ assertEquals(1, cash.count());
+ }
+
+ @Test
+ void testUpdate() {
+ //Arrange
+ var cash = new Cash(5);
+ //Act
+ cash.plus(6);
+ var result = cash.minus(3);
+ //Assert
+ assertTrue(result);
+ assertEquals(8, cash.count());
+ }
+}
+```
+
+## When to Use the Arrange/Act/Assert Pattern in Java
+
+Use Arrange/Act/Assert pattern when
+
+* Unit testing, especially within the context of TDD and BDD
+* Anywhere clarity and structure are needed in test cases
+
+## Real-World Applications of Arrange/Act/Assert Pattern in Java
+
+* This pattern is particularly useful when practicing TDD and/or BDD methodologies in Java.
+* Utilized in various programming languages and testing frameworks, such as JUnit (Java), NUnit (.NET), and xUnit frameworks.
+
+## Benefits and Trade-offs of Arrange/Act/Assert Pattern
+
+Benefits:
+
+* Improved readability of tests by clearly separating the setup, action, and verification steps.
+* Easier maintenance and understanding of tests, as each test is structured in a predictable way.
+* Facilitates debugging by isolating test failures to specific phases within the test.
+
+Trade-offs:
+
+* May introduce redundancy in tests, as similar arrangements may be repeated across tests.
+* Some complex tests might not fit neatly into this structure, requiring additional context or setup outside these three phases.
+
+## Related Java Design Patterns
+
+* [Page Object](https://java-design-patterns.com/patterns/page-object/): A pattern for organizing UI tests that can be used in conjunction with Arrange/Act/Assert.
+
+## References and Credits
+
+* [The Art of Unit Testing: with examples in C#](https://amzn.to/49IbdwO)
+* [Test Driven Development: By Example](https://amzn.to/3wEwKbF)
+* [Unit Testing Principles, Practices, and Patterns: Effective testing styles, patterns, and reliable automation for unit testing, mocking, and integration testing with examples in C#](https://amzn.to/4ayjpiM)
+* [xUnit Test Patterns: Refactoring Test Code](https://amzn.to/4dHGDpm)
+* [Arrange, Act, Assert: What is AAA Testing?](https://blog.ncrunch.net/post/arrange-act-assert-aaa-testing.aspx)
+* [Bill Wake: 3A – Arrange, Act, Assert (NCrunch)](https://xp123.com/articles/3a-arrange-act-assert/)
+* [GivenWhenThen (Martin Fowler)](https://martinfowler.com/bliki/GivenWhenThen.html)
diff --git a/arrange-act-assert/etc/arrange-act-assert-flowchart.png b/arrange-act-assert/etc/arrange-act-assert-flowchart.png
new file mode 100644
index 000000000000..8b7615352523
Binary files /dev/null and b/arrange-act-assert/etc/arrange-act-assert-flowchart.png differ
diff --git a/arrange-act-assert/etc/arrange-act-assert.urm.puml b/arrange-act-assert/etc/arrange-act-assert.urm.puml
new file mode 100644
index 000000000000..4b22d2a4e4bb
--- /dev/null
+++ b/arrange-act-assert/etc/arrange-act-assert.urm.puml
@@ -0,0 +1,11 @@
+@startuml
+package com.iluwatar.arrangeactassert {
+ class Cash {
+ - amount : int
+ ~ Cash(amount : int)
+ ~ count() : int
+ ~ minus(subtrahend : int) : boolean
+ ~ plus(addend : int)
+ }
+}
+@enduml
\ No newline at end of file
diff --git a/arrange-act-assert/pom.xml b/arrange-act-assert/pom.xml
new file mode 100644
index 000000000000..6b8ba1b2d490
--- /dev/null
+++ b/arrange-act-assert/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ arrange-act-assert
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
diff --git a/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
new file mode 100644
index 000000000000..0c31b1f89f44
--- /dev/null
+++ b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
@@ -0,0 +1,57 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.arrangeactassert;
+
+import lombok.AllArgsConstructor;
+
+/**
+ * Arrange/Act/Assert (AAA) is a unit test pattern. In this simple example, we have a ({@link Cash})
+ * object for plus, minus and counting amount.
+ */
+@AllArgsConstructor
+public class Cash {
+
+ private int amount;
+
+ // plus
+ void plus(int addend) {
+ amount += addend;
+ }
+
+ // minus
+ boolean minus(int subtrahend) {
+ if (amount >= subtrahend) {
+ amount -= subtrahend;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // count
+ int count() {
+ return amount;
+ }
+}
diff --git a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
new file mode 100644
index 000000000000..ebb261277080
--- /dev/null
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
@@ -0,0 +1,103 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.arrangeactassert;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Arrange/Act/Assert (AAA) is a pattern for organizing unit tests. It is a way to structure your
+ * tests, so they're easier to read, maintain and enhance.
+ *
+ *
It breaks tests down into three clear and distinct steps:
+ *
+ *
1. Arrange: Perform the setup and initialization required for the test.
+ *
+ *
2. Act: Take action(s) required for the test.
+ *
+ *
3. Assert: Verify the outcome(s) of the test.
+ *
+ *
This pattern has several significant benefits. It creates a clear separation between a test's
+ * setup, operations, and results. This structure makes the code easier to read and understand. If
+ * you place the steps in order and format your code to separate them, you can scan a test and
+ * quickly comprehend what it does.
+ *
+ *
It also enforces a certain degree of discipline when you write your tests. You have to think
+ * clearly about the three steps your test will perform. But it makes tests more natural to write at
+ * the same time since you already have an outline.
+ *
+ *
In ({@link CashAAATest}) we have four test methods. Each of them has only one reason to change
+ * and one reason to fail. In a large and complicated code base, tests that honor the single
+ * responsibility principle are much easier to troubleshoot.
+ */
+class CashAAATest {
+
+ @Test
+ void testPlus() {
+ // Arrange
+ var cash = new Cash(3);
+ // Act
+ cash.plus(4);
+ // Assert
+ assertEquals(7, cash.count());
+ }
+
+ @Test
+ void testMinus() {
+ // Arrange
+ var cash = new Cash(8);
+ // Act
+ var result = cash.minus(5);
+ // Assert
+ assertTrue(result);
+ assertEquals(3, cash.count());
+ }
+
+ @Test
+ void testInsufficientMinus() {
+ // Arrange
+ var cash = new Cash(1);
+ // Act
+ var result = cash.minus(6);
+ // Assert
+ assertFalse(result);
+ assertEquals(1, cash.count());
+ }
+
+ @Test
+ void testUpdate() {
+ // Arrange
+ var cash = new Cash(5);
+ // Act
+ cash.plus(6);
+ var result = cash.minus(3);
+ // Assert
+ assertTrue(result);
+ assertEquals(8, cash.count());
+ }
+}
diff --git a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
new file mode 100644
index 000000000000..5756822516b8
--- /dev/null
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
@@ -0,0 +1,60 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.arrangeactassert;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * ({@link CashAAATest}) is an anti-example of AAA pattern. This test is functionally correct, but
+ * with the addition of a new feature, it needs refactoring. There are an awful lot of steps in that
+ * test method, but it verifies the class' important behavior in just eleven lines. It violates the
+ * single responsibility principle. If this test method failed after a small code change, it might
+ * take some digging to discover why.
+ */
+class CashAntiAAATest {
+
+ @Test
+ void testCash() {
+ // initialize
+ var cash = new Cash(3);
+ // test plus
+ cash.plus(4);
+ assertEquals(7, cash.count());
+ // test minus
+ cash = new Cash(8);
+ assertTrue(cash.minus(5));
+ assertEquals(3, cash.count());
+ assertFalse(cash.minus(6));
+ assertEquals(3, cash.count());
+ // test update
+ cash.plus(5);
+ assertTrue(cash.minus(5));
+ assertEquals(3, cash.count());
+ }
+}
diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md
index b96c1d77a925..519152e1268c 100644
--- a/async-method-invocation/README.md
+++ b/async-method-invocation/README.md
@@ -1,34 +1,217 @@
---
-layout: pattern
-title: Async Method Invocation
-folder: async-method-invocation
-permalink: /patterns/async-method-invocation/
-pumlid: TSdB3SCW303GLTe1mFTkunWhk0A3_4dKxTi5UdlIUuhIoCPfuz4Zjhy03EzwIlGyqjbeQR16fJL1HjuOQF362qjZbrFBnWWsTPZeFm3wHwbCZhvQ4RqMOSXIuA1_LzDctJd75m00
-categories: Concurrency
-tags:
- - Java
- - Difficulty-Intermediate
- - Functional
- - Reactive
+title: "Async Method Invocation Pattern in Java: Elevate Performance with Asynchronous Programming"
+shortTitle: Async Method Invocation
+description: "Learn about the Async Method Invocation pattern in Java for asynchronous method calls, enhancing concurrency, scalability, and responsiveness in your applications. Explore real-world examples and code implementations."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Decoupling
+ - Reactive
+ - Scalability
+ - Thread management
---
-## Intent
-Asynchronous method invocation is pattern where the calling thread
-is not blocked while waiting results of tasks. The pattern provides parallel
-processing of multiple independent tasks and retrieving the results via
-callbacks or waiting until everything is done.
+## Also known as
-
+* Asynchronous Procedure Call
-## Applicability
-Use async method invocation pattern when
+## Intent of Async Method Invocation Design Pattern
-* you have multiple independent tasks that can run in parallel
-* you need to improve the performance of a group of sequential tasks
-* you have limited amount of processing capacity or long running tasks and the
- caller should not wait the tasks to be ready
+The Async Method Invocation pattern is designed to enhance concurrency by allowing methods to be called asynchronously. This pattern helps in executing parallel tasks, reducing wait times, and improving system throughput.
-## Real world examples
+## Detailed Explanation of Async Method Invocation Pattern with Real-World Examples
-* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html), [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java)
-* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET)
+Real-world example
+
+> Asynchronous method invocation enables non-blocking operations, allowing multiple processes to run concurrently. This pattern is particularly useful in applications requiring high scalability and performance, such as web servers and microservices.
+>
+> In the context of space rockets, an analogous example of the Async Method Invocation pattern can be seen in the communication between the mission control center and the onboard systems of the rocket. When mission control sends a command to the rocket to adjust its trajectory or perform a system check, they do not wait idly for the rocket to complete the task and report back. Instead, mission control continues to monitor other aspects of the mission and manage different tasks. The rocket executes the command asynchronously and sends a status update or result back to mission control once the operation is complete. This allows mission control to efficiently manage multiple concurrent operations without being blocked by any single task, similar to how asynchronous method invocation works in software systems.
+
+In plain words
+
+> Asynchronous method invocation starts task processing and returns immediately before the task is ready. The results of the task processing are returned to the caller later.
+
+Wikipedia says
+
+> In multithreaded computer programming, asynchronous method invocation (AMI), also known as asynchronous method calls or the asynchronous pattern is a design pattern in which the call site is not blocked while waiting for the called code to finish. Instead, the calling thread is notified when the reply arrives. Polling for a reply is an undesired option.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Async Method Invocation Pattern in Java
+
+Consider a scenario where multiple tasks need to be executed simultaneously. Using the Async Method Invocation pattern, you can initiate these tasks without waiting for each to complete, thus optimizing resource usage and reducing latency.
+
+In this example, we are launching space rockets and deploying lunar rovers.
+
+The application demonstrates the async method invocation pattern. The key parts of the pattern are`AsyncResult` which is an intermediate container for an asynchronously evaluated value, `AsyncCallback` which can be provided to be executed on task completion and `AsyncExecutor` that manages the execution of the async tasks.
+
+```java
+public interface AsyncResult {
+ boolean isCompleted();
+
+ T getValue() throws ExecutionException;
+
+ void await() throws InterruptedException;
+}
+```
+
+```java
+public interface AsyncCallback {
+ void onComplete(T value);
+
+ void onError(Exception ex);
+}
+```
+
+```java
+public interface AsyncExecutor {
+ AsyncResult startProcess(Callable task);
+
+ AsyncResult startProcess(Callable task, AsyncCallback callback);
+
+ T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException;
+}
+```
+
+`ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted next.
+
+```java
+public class ThreadAsyncExecutor implements AsyncExecutor {
+
+ @Override
+ public AsyncResult startProcess(Callable task) {
+ return startProcess(task, null);
+ }
+
+ @Override
+ public AsyncResult startProcess(Callable task, AsyncCallback callback) {
+ var result = new CompletableResult<>(callback);
+ new Thread(
+ () -> {
+ try {
+ result.setValue(task.call());
+ } catch (Exception ex) {
+ result.setException(ex);
+ }
+ },
+ "executor-" + idx.incrementAndGet())
+ .start();
+ return result;
+ }
+
+ @Override
+ public T endProcess(AsyncResult asyncResult)
+ throws ExecutionException, InterruptedException {
+ if (!asyncResult.isCompleted()) {
+ asyncResult.await();
+ }
+ return asyncResult.getValue();
+ }
+}
+```
+
+Then we are ready to launch some rockets to see how everything works together.
+
+```java
+ public static void main(String[] args) throws Exception {
+ // construct a new executor that will run async tasks
+ var executor = new ThreadAsyncExecutor();
+
+ // start few async tasks with varying processing times, two last with callback handlers
+ final var asyncResult1 = executor.startProcess(lazyval(10, 500));
+ final var asyncResult2 = executor.startProcess(lazyval("test", 300));
+ final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
+ final var asyncResult4 = executor.startProcess(lazyval(20, 400),
+ callback("Deploying lunar rover"));
+ final var asyncResult5 =
+ executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
+
+ // emulate processing in the current thread while async tasks are running in their own threads
+ Thread.sleep(350); // Oh boy, we are working hard here
+ log("Mission command is sipping coffee");
+
+ // wait for completion of the tasks
+ final var result1 = executor.endProcess(asyncResult1);
+ final var result2 = executor.endProcess(asyncResult2);
+ final var result3 = executor.endProcess(asyncResult3);
+ asyncResult4.await();
+ asyncResult5.await();
+
+ // log the results of the tasks, callbacks log immediately when complete
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
+}
+```
+
+Here's the program console output.
+
+```
+21:47:08.227[executor-2]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully
+21:47:08.269[main]INFO com.iluwatar.async.method.invocation.App-Mission command is sipping coffee
+21:47:08.318[executor-4]INFO com.iluwatar.async.method.invocation.App-Space rocket<20>launched successfully
+21:47:08.335[executor-4]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<20>
+21:47:08.414[executor-1]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launched successfully
+21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully
+21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover
+21:47:08.616[executor-3]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launched successfully
+21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launch complete
+21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket launch complete
+21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete
+```
+
+## When to Use the Async Method Invocation Pattern in Java
+
+This pattern is ideal for applications needing to manage multiple parallel tasks efficiently. It is commonly used in scenarios such as handling background processes, improving user interface responsiveness, and managing asynchronous data processing.
+
+Use the async method invocation pattern when
+
+* When operations do not need to complete before proceeding with the next steps in a program.
+* For tasks that are resource-intensive or time-consuming, such as IO operations, network requests, or complex computations, where making the operation synchronous would significantly impact performance or user experience.
+* In GUI applications to prevent freezing or unresponsiveness during long-running tasks.
+* In web applications for non-blocking IO operations.
+
+## Real-World Applications of Async Method Invocation Pattern in Java
+
+Many modern applications leverage the Async Method Invocation pattern, including web servers handling concurrent requests, microservices architectures, and systems requiring intensive background processing.
+
+* Web servers handling HTTP requests asynchronously to improve throughput and reduce latency.
+* Desktop and mobile applications using background threads to perform time-consuming operations without blocking the user interface.
+* Microservices architectures where services perform asynchronous communications via messaging queues or event streams.
+* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html)
+* [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)
+* [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)
+* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx)
+
+## Benefits and Trade-offs of Async Method Invocation Pattern
+
+While this pattern offers significant performance benefits, it also introduces complexity in error handling and resource management. Proper implementation is essential to avoid potential pitfalls such as race conditions and deadlocks.
+
+Benefits:
+
+* Improved Responsiveness: The main thread or application flow remains unblocked, improving the user experience in GUI applications and overall responsiveness.
+* Better Resource Utilization: By enabling parallel execution, system resources (like CPU and IO) are utilized more efficiently, potentially improving the application's throughput.
+* Scalability: Easier to scale applications, as tasks can be distributed across available resources more effectively.
+
+Trade-offs:
+
+* Complexity: Introducing asynchronous operations can complicate the codebase, making it more challenging to understand, debug, and maintain.
+* Resource Management: Requires careful management of threads or execution contexts, which can introduce overhead and potential resource exhaustion issues.
+* Error Handling: Asynchronous operations can make error handling more complex, as exceptions may occur in different threads or at different times.
+
+## Related Java Design Patterns
+
+The Async Method Invocation pattern often works well with other design patterns like the Command Pattern for encapsulating requests, the Observer Pattern for event handling, and the Promise Pattern for managing asynchronous results.
+
+* [Command](https://java-design-patterns.com/patterns/command/): Asynchronous method invocation can be used to implement the Command pattern, where commands are executed asynchronously.
+* [Observer](https://java-design-patterns.com/patterns/observer/): Asynchronous method invocation can be used to notify observers asynchronously when a subject's state changes.
+* [Promise](https://java-design-patterns.com/patterns/promise/): The AsyncResult interface can be considered a form of Promise, representing a value that may not be available yet.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3Ti1N4f)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Java Concurrency in Practice](https://amzn.to/4ab97VU)
diff --git a/async-method-invocation/etc/async-method-invocation-sequence-diagram.png b/async-method-invocation/etc/async-method-invocation-sequence-diagram.png
new file mode 100644
index 000000000000..28420d783379
Binary files /dev/null and b/async-method-invocation/etc/async-method-invocation-sequence-diagram.png differ
diff --git a/async-method-invocation/etc/async-method-invocation.ucls b/async-method-invocation/etc/async-method-invocation.ucls
deleted file mode 100644
index f2e189eaeae0..000000000000
--- a/async-method-invocation/etc/async-method-invocation.ucls
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/async-method-invocation/etc/async-method-invocation.urm.png b/async-method-invocation/etc/async-method-invocation.urm.png
new file mode 100644
index 000000000000..898c1e199bda
Binary files /dev/null and b/async-method-invocation/etc/async-method-invocation.urm.png differ
diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml
index 6f5d0b27f9d4..d44665081856 100644
--- a/async-method-invocation/etc/async-method-invocation.urm.puml
+++ b/async-method-invocation/etc/async-method-invocation.urm.puml
@@ -9,7 +9,8 @@ package com.iluwatar.async.method.invocation {
+ main(args : String[]) {static}
}
interface AsyncCallback {
- + onComplete(T, Optional) {abstract}
+ + onComplete(T) {abstract}
+ + onError(Exception) {abstract}
}
interface AsyncExecutor {
+ endProcess(AsyncResult) : T {abstract}
@@ -32,7 +33,7 @@ package com.iluwatar.async.method.invocation {
~ COMPLETED : int {static}
~ FAILED : int {static}
~ RUNNING : int {static}
- ~ callback : Optional>
+ ~ callback : AsyncCallback
~ exception : Exception
~ lock : Object
~ state : int
@@ -40,12 +41,15 @@ package com.iluwatar.async.method.invocation {
~ CompletableResult(callback : AsyncCallback)
+ await()
+ getValue() : T
+ ~ hasCallback() : boolean
+ isCompleted() : boolean
~ setException(exception : Exception)
~ setValue(value : T)
}
}
+ThreadAsyncExecutor ..|> AsyncCallback
+ThreadAsyncExecutor ..|> AsyncResult
+ThreadAsyncExecutor ..|> AsyncExecutor
CompletableResult ..+ ThreadAsyncExecutor
-ThreadAsyncExecutor ..|> AsyncExecutor
-CompletableResult ..|> AsyncResult
-@enduml
\ No newline at end of file
+CompletableResult ..|> AsyncResult
+@enduml
diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml
index 7e15aff73e3b..d9ddd918c8e9 100644
--- a/async-method-invocation/pom.xml
+++ b/async-method-invocation/pom.xml
@@ -1,8 +1,10 @@
-
+
-
+
4.0.0
com.iluwatar
java-design-patterns
- 1.18.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
async-method-invocation
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
@@ -44,4 +53,23 @@
test
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.async.method.invocation.App
+
+
+
+
+
+
+
+
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
index 0a387d58296a..ec3beed3be4d 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,87 +24,86 @@
*/
package com.iluwatar.async.method.invocation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.concurrent.Callable;
+import lombok.extern.slf4j.Slf4j;
/**
- * This application demonstrates the async method invocation pattern. Key parts of the pattern are
- * AsyncResult
which is an intermediate container for an asynchronously evaluated value,
- * AsyncCallback
which can be provided to be executed on task completion and AsyncExecutor
- * that manages the execution of the async tasks.
- *
- * The main method shows example flow of async invocations. The main thread starts multiple tasks with variable
- * durations and then continues its own work. When the main thread has done it's job it collects the results of the
- * async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the
- * tasks complete.
- *
- * Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected
- * in the main thread but the callbacks are executed within the worker threads. This should be noted when working with
- * thread pools.
- *
- * Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and
- * ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming,
- * the implementations are not trivial. This example does not take all possible scenarios into account but rather
- * provides a simple version that helps to understand the pattern.
+ * In this example, we are launching space rockets and deploying lunar rovers.
+ *
+ *
The application demonstrates the async method invocation pattern. The key parts of the pattern
+ * are AsyncResult
which is an intermediate container for an asynchronously evaluated
+ * value, AsyncCallback
which can be provided to be executed on task completion and
+ * AsyncExecutor
that manages the execution of the async tasks.
+ *
+ *
The main method shows example flow of async invocations. The main thread starts multiple tasks
+ * with variable durations and then continues its own work. When the main thread has done it's job
+ * it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
+ * the callbacks are executed immediately when the tasks complete.
+ *
+ *
Noteworthy difference of thread usage between the async results and callbacks is that the
+ * async results are collected in the main thread but the callbacks are executed within the worker
+ * threads. This should be noted when working with thread pools.
+ *
+ *
Java provides its own implementations of async method invocation pattern. FutureTask,
+ * CompletableFuture and ExecutorService are the real world implementations of this pattern. But due
+ * to the nature of parallel programming, the implementations are not trivial. This example does not
+ * take all possible scenarios into account but rather provides a simple version that helps to
+ * understand the pattern.
*
* @see AsyncResult
* @see AsyncCallback
* @see AsyncExecutor
- *
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
* @see java.util.concurrent.ExecutorService
*/
+@Slf4j
public class App {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+ private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
- /**
- * Program entry point
- */
+ /** Program entry point. */
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
- AsyncExecutor executor = new ThreadAsyncExecutor();
+ var executor = new ThreadAsyncExecutor();
// start few async tasks with varying processing times, two last with callback handlers
- AsyncResult asyncResult1 = executor.startProcess(lazyval(10, 500));
- AsyncResult asyncResult2 = executor.startProcess(lazyval("test", 300));
- AsyncResult asyncResult3 = executor.startProcess(lazyval(50L, 700));
- AsyncResult asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
- AsyncResult asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
+ final var asyncResult1 = executor.startProcess(lazyval(10, 500));
+ final var asyncResult2 = executor.startProcess(lazyval("test", 300));
+ final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
+ final var asyncResult4 =
+ executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
+ final var asyncResult5 =
+ executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
// emulate processing in the current thread while async tasks are running in their own threads
- Thread.sleep(350); // Oh boy I'm working hard here
- log("Some hard work done");
+ Thread.sleep(350); // Oh boy, we are working hard here
+ log("Mission command is sipping coffee");
// wait for completion of the tasks
- Integer result1 = executor.endProcess(asyncResult1);
- String result2 = executor.endProcess(asyncResult2);
- Long result3 = executor.endProcess(asyncResult3);
+ final var result1 = executor.endProcess(asyncResult1);
+ final var result2 = executor.endProcess(asyncResult2);
+ final var result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
asyncResult5.await();
// log the results of the tasks, callbacks log immediately when complete
- log("Result 1: " + result1);
- log("Result 2: " + result2);
- log("Result 3: " + result3);
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
}
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
- * @param value
- * value to evaluate
- * @param delayMillis
- * artificial delay in milliseconds
+ * @param value value to evaluate
+ * @param delayMillis artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
private static Callable lazyval(T value, long delayMillis) {
return () -> {
Thread.sleep(delayMillis);
- log("Task completed with: " + value);
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
return value;
};
}
@@ -110,16 +111,19 @@ private static Callable lazyval(T value, long delayMillis) {
/**
* Creates a simple callback that logs the complete status of the async result.
*
- * @param name
- * callback name
+ * @param name callback name
* @return new async callback
*/
private static AsyncCallback callback(String name) {
- return (value, ex) -> {
- if (ex.isPresent()) {
- log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
- } else {
- log(name + ": " + value);
+ return new AsyncCallback<>() {
+ @Override
+ public void onComplete(T value) {
+ log(name + " <" + value + ">");
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ log(name + " failed: " + ex.getMessage());
}
};
}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
index 5a291f996d9a..2dfbaf9a188a 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,22 +24,24 @@
*/
package com.iluwatar.async.method.invocation;
-import java.util.Optional;
-
/**
- *
- * AsyncCallback interface
+ * AsyncCallback interface.
*
- * @param
- *
+ * @param Type of Result
*/
public interface AsyncCallback {
/**
- * Complete handler which is executed when async task is completed or fails execution.
+ * Complete handler which is executed when async task is completed.
+ *
+ * @param value the evaluated value from async task
+ */
+ void onComplete(T value);
+
+ /**
+ * Error handler which is executed when async task fails execution.
*
- * @param value the evaluated value from async task, undefined when execution fails
- * @param ex empty value if execution succeeds, some exception if executions fails
+ * @param ex exception which was thrown during async task execution(non-null)
*/
- void onComplete(T value, Optional ex);
+ void onError(Exception ex);
}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
index 800bc2728fad..3bae90830098 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -25,11 +27,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-/**
- *
- * AsyncExecutor interface
- *
- */
+/** AsyncExecutor interface. */
public interface AsyncExecutor {
/**
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
index f7fd9ab61e30..3eebdc4e773d 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -25,7 +27,8 @@
import java.util.concurrent.ExecutionException;
/**
- * AsyncResult interface
+ * AsyncResult interface.
+ *
* @param parameter returned when getValue is invoked
*/
public interface AsyncResult {
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
index d12ebbe19297..a1261f34184c 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,19 +24,14 @@
*/
package com.iluwatar.async.method.invocation;
-import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
-/**
- *
- * Implementation of async executor that creates a new thread for every task.
- *
- */
+/** Implementation of async executor that creates a new thread for every task. */
public class ThreadAsyncExecutor implements AsyncExecutor {
- /** Index for thread naming */
+ /** Index for thread naming. */
private final AtomicInteger idx = new AtomicInteger(0);
@Override
@@ -44,19 +41,23 @@ public AsyncResult startProcess(Callable task) {
@Override
public AsyncResult startProcess(Callable task, AsyncCallback callback) {
- CompletableResult result = new CompletableResult<>(callback);
- new Thread(() -> {
- try {
- result.setValue(task.call());
- } catch (Exception ex) {
- result.setException(ex);
- }
- } , "executor-" + idx.incrementAndGet()).start();
+ var result = new CompletableResult<>(callback);
+ new Thread(
+ () -> {
+ try {
+ result.setValue(task.call());
+ } catch (Exception ex) {
+ result.setException(ex);
+ }
+ },
+ "executor-" + idx.incrementAndGet())
+ .start();
return result;
}
@Override
- public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException {
+ public T endProcess(AsyncResult asyncResult)
+ throws ExecutionException, InterruptedException {
if (!asyncResult.isCompleted()) {
asyncResult.await();
}
@@ -64,8 +65,9 @@ public T endProcess(AsyncResult asyncResult) throws ExecutionException, I
}
/**
- * Simple implementation of async result that allows completing it successfully with a value or exceptionally with an
- * exception. A really simplified version from its real life cousins FutureTask and CompletableFuture.
+ * Simple implementation of async result that allows completing it successfully with a value or
+ * exceptionally with an exception. A really simplified version from its real life cousins
+ * FutureTask and CompletableFuture.
*
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
@@ -77,7 +79,7 @@ private static class CompletableResult implements AsyncResult {
static final int COMPLETED = 3;
final Object lock;
- final Optional> callback;
+ final AsyncCallback callback;
volatile int state = RUNNING;
T value;
@@ -85,36 +87,42 @@ private static class CompletableResult implements AsyncResult {
CompletableResult(AsyncCallback callback) {
this.lock = new Object();
- this.callback = Optional.ofNullable(callback);
+ this.callback = callback;
+ }
+
+ boolean hasCallback() {
+ return callback != null;
}
/**
- * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
- * completion.
+ * Sets the value from successful execution and executes callback if available. Notifies any
+ * thread waiting for completion.
*
- * @param value
- * value of the evaluated task
+ * @param value value of the evaluated task
*/
void setValue(T value) {
this.value = value;
this.state = COMPLETED;
- this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty()));
+ if (hasCallback()) {
+ callback.onComplete(value);
+ }
synchronized (lock) {
lock.notifyAll();
}
}
/**
- * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
- * completion.
+ * Sets the exception from failed execution and executes callback if available. Notifies any
+ * thread waiting for completion.
*
- * @param exception
- * exception of the failed task
+ * @param exception exception of the failed task
*/
void setException(Exception exception) {
this.exception = exception;
this.state = FAILED;
- this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception)));
+ if (hasCallback()) {
+ callback.onError(exception);
+ }
synchronized (lock) {
lock.notifyAll();
}
diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
index ef2649f8afd6..d58a3f6b5c30 100644
--- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
+++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,18 +24,22 @@
*/
package com.iluwatar.async.method.invocation;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- *
- * Application test
- *
- */
-public class AppTest {
+import org.junit.jupiter.api.Test;
+/** Application test */
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
+ */
@Test
- public void test() throws Exception {
- String[] args = {};
- App.main(args);
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java
index d0e730344b6c..d6540ce77309 100644
--- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java
+++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java
@@ -1,6 +1,8 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014-2016 Ilkka Seppälä
+ * Copyright © 2014-2022 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
@@ -22,290 +24,326 @@
*/
package com.iluwatar.async.method.invocation;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Matchers;
+import static java.time.Duration.ofMillis;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
-import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
+/** ThreadAsyncExecutorTest */
+class ThreadAsyncExecutorTest {
-/**
- * Date: 12/6/15 - 10:49 AM
- *
- * @author Jeroen Meulemeester
- */
-public class ThreadAsyncExecutorTest {
+ @Captor private ArgumentCaptor exceptionCaptor;
- /**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
- */
- @Test(timeout = 3000)
- public void testSuccessfulTaskWithoutCallback() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
-
- final Object result = new Object();
- final Callable task = mock(Callable.class);
- when(task.call()).thenReturn(result);
+ @Mock private Callable task;
- final AsyncResult asyncResult = executor.startProcess(task);
- assertNotNull(asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
+ @Mock private AsyncCallback callback;
- // Our task should only execute once ...
- verify(task, times(1)).call();
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
+ /** Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} */
+ @Test
+ void testSuccessfulTaskWithoutCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call()).thenReturn(result);
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ // Our task should only execute once ...
+ verify(task, times(1)).call();
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)}
*/
- @Test(timeout = 3000)
- public void testSuccessfulTaskWithCallback() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
-
- final Object result = new Object();
- final Callable task = mock(Callable.class);
- when(task.call()).thenReturn(result);
-
- final AsyncCallback callback = mock(AsyncCallback.class);
- final AsyncResult asyncResult = executor.startProcess(task, callback);
- assertNotNull(asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- // Our task should only execute once ...
- verify(task, times(1)).call();
-
- // ... same for the callback, we expect our object
- final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
- verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
-
- final Optional optionalException = optionalCaptor.getValue();
- assertNotNull(optionalException);
- assertFalse(optionalException.isPresent());
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
+ @Test
+ void testSuccessfulTaskWithCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call()).thenReturn(result);
+
+ final var asyncResult = executor.startProcess(task, callback);
+ assertNotNull(asyncResult);
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ // Our task should only execute once ...
+ verify(task, times(1)).call();
+
+ // ... same for the callback, we expect our object
+ verify(callback, times(1)).onComplete(eq(result));
+ verify(callback, times(0)).onError(exceptionCaptor.capture());
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
- * to execute
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a
+ * task takes a while to execute
*/
- @Test(timeout = 5000)
- public void testLongRunningTaskWithoutCallback() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
-
- final Object result = new Object();
- final Callable task = mock(Callable.class);
- when(task.call()).thenAnswer(i -> {
- Thread.sleep(1500);
- return result;
- });
-
- final AsyncResult asyncResult = executor.startProcess(task);
- assertNotNull(asyncResult);
- assertFalse(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
- } catch (IllegalStateException e) {
- assertNotNull(e.getMessage());
- }
-
- // Our task should only execute once, but it can take a while ...
- verify(task, timeout(3000).times(1)).call();
-
- // Prevent timing issues, and wait until the result is available
- asyncResult.await();
- assertTrue(asyncResult.isCompleted());
- verifyNoMoreInteractions(task);
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
+ @Test
+ void testLongRunningTaskWithoutCallback() {
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Our task should only execute once, but it can take a while ...
+ verify(task, timeout(3000).times(1)).call();
+
+ // Prevent timing issues, and wait until the result is available
+ asyncResult.await();
+ assertTrue(asyncResult.isCompleted());
+ verifyNoMoreInteractions(task);
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task
- * takes a while to execute
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)} when a task takes a while to execute
*/
- @Test(timeout = 5000)
- public void testLongRunningTaskWithCallback() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
-
- final Object result = new Object();
- final Callable task = mock(Callable.class);
- when(task.call()).thenAnswer(i -> {
- Thread.sleep(1500);
- return result;
- });
-
- final AsyncCallback callback = mock(AsyncCallback.class);
- final AsyncResult asyncResult = executor.startProcess(task, callback);
- assertNotNull(asyncResult);
- assertFalse(asyncResult.isCompleted());
-
- verifyZeroInteractions(callback);
-
- try {
- asyncResult.getValue();
- fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
- } catch (IllegalStateException e) {
- assertNotNull(e.getMessage());
- }
-
- // Our task should only execute once, but it can take a while ...
- verify(task, timeout(3000).times(1)).call();
-
- final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
- verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
-
- final Optional optionalException = optionalCaptor.getValue();
- assertNotNull(optionalException);
- assertFalse(optionalException.isPresent());
-
- // Prevent timing issues, and wait until the result is available
- asyncResult.await();
- assertTrue(asyncResult.isCompleted());
- verifyNoMoreInteractions(task, callback);
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
+ @Test
+ void testLongRunningTaskWithCallback() {
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task, callback);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ verifyNoMoreInteractions(callback);
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Our task should only execute once, but it can take a while ...
+ verify(task, timeout(3000).times(1)).call();
+ verify(callback, timeout(3000).times(1)).onComplete(eq(result));
+ verify(callback, times(0)).onError(isA(Exception.class));
+
+ // Prevent timing issues, and wait until the result is available
+ asyncResult.await();
+ assertTrue(asyncResult.isCompleted());
+ verifyNoMoreInteractions(task, callback);
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
- * to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a
+ * task takes a while to execute, while waiting on the result using {@link
+ * ThreadAsyncExecutor#endProcess(AsyncResult)}
*/
- @Test(timeout = 5000)
- public void testEndProcess() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
-
- final Object result = new Object();
- final Callable task = mock(Callable.class);
- when(task.call()).thenAnswer(i -> {
- Thread.sleep(1500);
- return result;
- });
-
- final AsyncResult asyncResult = executor.startProcess(task);
- assertNotNull(asyncResult);
- assertFalse(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
- } catch (IllegalStateException e) {
- assertNotNull(e.getMessage());
- }
-
- assertSame(result, executor.endProcess(asyncResult));
- verify(task, times(1)).call();
- assertTrue(asyncResult.isCompleted());
-
- // Calling end process a second time while already finished should give the same result
- assertSame(result, executor.endProcess(asyncResult));
- verifyNoMoreInteractions(task);
+ @Test
+ void testEndProcess() {
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ assertSame(result, executor.endProcess(asyncResult));
+ verify(task, times(1)).call();
+ assertTrue(asyncResult.isCompleted());
+
+ // Calling end process a second time while already finished should give the same result
+ assertSame(result, executor.endProcess(asyncResult));
+ verifyNoMoreInteractions(task);
+ });
}
/**
- * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null'
+ * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when
+ * the callable is 'null'
*/
- @Test(timeout = 3000)
- public void testNullTask() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
- final AsyncResult asyncResult = executor.startProcess(null);
-
- assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected ExecutionException with NPE as cause");
- } catch (final ExecutionException e) {
- assertNotNull(e.getMessage());
- assertNotNull(e.getCause());
- assertEquals(NullPointerException.class, e.getCause().getClass());
- }
-
+ @Test
+ void testNullTask() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task was 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
}
/**
- * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the
- * callable is 'null', but the asynchronous callback is provided
+ * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)} when the callable is 'null', but the asynchronous callback is provided
*/
- @Test(timeout = 3000)
- public void testNullTaskWithCallback() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
- final AsyncCallback callback = mock(AsyncCallback.class);
- final AsyncResult asyncResult = executor.startProcess(null, callback);
-
- assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
- verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
-
- final Optional optionalException = optionalCaptor.getValue();
- assertNotNull(optionalException);
- assertTrue(optionalException.isPresent());
-
- final Exception exception = optionalException.get();
- assertNotNull(exception);
- assertEquals(NullPointerException.class, exception.getClass());
-
- try {
- asyncResult.getValue();
- fail("Expected ExecutionException with NPE as cause");
- } catch (final ExecutionException e) {
- assertNotNull(e.getMessage());
- assertNotNull(e.getCause());
- assertEquals(NullPointerException.class, e.getCause().getClass());
- }
-
+ @Test
+ void testNullTaskWithCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null, callback);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task was 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+ verify(callback, times(0)).onComplete(any());
+ verify(callback, times(1)).onError(exceptionCaptor.capture());
+
+ final var exception = exceptionCaptor.getValue();
+ assertNotNull(exception);
+
+ assertEquals(NullPointerException.class, exception.getClass());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
}
/**
- * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both
- * the callable and the asynchronous callback are 'null'
+ * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)} when both the callable and the asynchronous callback are 'null'
*/
- @Test(timeout = 3000)
- public void testNullTaskWithNullCallback() throws Exception {
- // Instantiate a new executor and start a new 'null' task ...
- final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
- final AsyncResult asyncResult = executor.startProcess(null, null);
-
- assertNotNull("The AsyncResult should not be 'null', even though the task and callback were 'null'.", asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected ExecutionException with NPE as cause");
- } catch (final ExecutionException e) {
- assertNotNull(e.getMessage());
- assertNotNull(e.getCause());
- assertEquals(NullPointerException.class, e.getCause().getClass());
- }
-
+ @Test
+ void testNullTaskWithNullCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null, null);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task and callback were 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
}
-
-}
\ No newline at end of file
+}
diff --git a/backpressure/README.md b/backpressure/README.md
new file mode 100644
index 000000000000..d0734ad1f934
--- /dev/null
+++ b/backpressure/README.md
@@ -0,0 +1,156 @@
+---
+title: "Backpressure Pattern in Java: Gracefully regulate producer-to-consumer data flow to prevent overload."
+shortTitle: Backpressure
+description: "Dive into the Backpressure design pattern in Java through practical examples, discovering how it prevents overload while ensuring stability and peak performance by aligning data flow with consumer capacity."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Event-driven
+ - Reactive
+ - Resilience
+---
+
+## Also known as
+
+* Flow Control
+* Rate Limiting Mechanism
+
+## Intent of the Backpressure Design Pattern
+
+Control the rate of data production so downstream consumers are not overwhelmed by excessive load.
+
+## Detailed Explanation of Backpressure Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy coffee shop where multiple baristas brew drinks (producers), and a single barista is responsible for carefully crafting latte art (consumer). If drinks are brewed faster than the latte-art barista can decorate them, they pile up, risking quality issues or discarded drinks. By introducing a pacing system—only sending new cups once the latte-art barista is ready—everyone stays synchronized, ensuring minimal waste and a consistently enjoyable customer experience.
+
+In plain words
+
+> The Backpressure design pattern is a flow control mechanism that prevents overwhelming a system by regulating data production based on the consumer’s processing capacity.
+
+Wikipedia says
+
+> Back pressure (or backpressure) is the term for a resistance to the desired flow of fluid through pipes. Obstructions or tight bends create backpressure via friction loss and pressure drop. In distributed systems in particular event-driven architecture, back pressure is a technique to regulate flow of data, ensuring that components do not become overwhelmed.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Backpressure Pattern in Java
+
+This example demonstrates how backpressure can be implemented using Project Reactor. We begin by creating a simple publisher that emits a stream of integers, introducing a small delay to mimic a slower production rate:
+
+```java
+public class Publisher {
+ public static Flux publish(int start, int count, int delay) {
+ return Flux.range(start, count).delayElements(Duration.ofMillis(delay)).log();
+ }
+}
+```
+
+Next, we define a custom subscriber by extending Reactor’s BaseSubscriber. It simulates slow processing by sleeping for 500ms per item. Initially, the subscriber requests ten items; for every five items processed, it requests five more:
+
+```java
+public class Subscriber extends BaseSubscriber {
+
+ private static final Logger logger = LoggerFactory.getLogger(Subscriber.class);
+
+ @Override
+ protected void hookOnSubscribe(@NonNull Subscription subscription) {
+ logger.info("subscribe()");
+ request(10); //request 10 items initially
+ }
+
+ @Override
+ protected void hookOnNext(@NonNull Integer value) {
+ processItem();
+ logger.info("process({})", value);
+ if (value % 5 == 0) {
+ // request for the next 5 items after processing first 5
+ request(5);
+ }
+ }
+
+ @Override
+ protected void hookOnComplete() {
+ //completed processing.
+ }
+
+ private void processItem() {
+ try {
+ Thread.sleep(500); // simulate slow processing
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+}
+```
+
+Finally, in the `main` method, we publish a range of integers and subscribe using the custom subscriber. A short sleep in the main thread allows the emission, backpressure requests, and processing to be fully observed:
+
+```java
+public static void main(String[] args) throws InterruptedException {
+ Subscriber sub = new Subscriber();
+ Publisher.publish(1, 8, 200).subscribe(sub);
+ Thread.sleep(5000); //wait for execution
+
+}
+```
+
+Below is an example of the program’s output. It shows the subscriber’s log entries, including when it requests more data and when each integer is processed:
+
+```
+23:09:55.746 [main] DEBUG reactor.util.Loggers -- Using Slf4j logging framework
+23:09:55.762 [main] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onSubscribe(FluxConcatMapNoPrefetch.FluxConcatMapNoPrefetchSubscriber)
+23:09:55.762 [main] INFO com.iluwatar.backpressure.Subscriber -- subscribe()
+23:09:55.763 [main] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- request(10)
+23:09:55.969 [parallel-1] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(1)
+23:09:56.475 [parallel-1] INFO com.iluwatar.backpressure.Subscriber -- process(1)
+23:09:56.680 [parallel-2] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(2)
+23:09:57.185 [parallel-2] INFO com.iluwatar.backpressure.Subscriber -- process(2)
+23:09:57.389 [parallel-3] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(3)
+23:09:57.894 [parallel-3] INFO com.iluwatar.backpressure.Subscriber -- process(3)
+23:09:58.099 [parallel-4] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(4)
+23:09:58.599 [parallel-4] INFO com.iluwatar.backpressure.Subscriber -- process(4)
+23:09:58.805 [parallel-5] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(5)
+23:09:59.311 [parallel-5] INFO com.iluwatar.backpressure.Subscriber -- process(5)
+23:09:59.311 [parallel-5] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- request(5)
+23:09:59.516 [parallel-6] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(6)
+23:10:00.018 [parallel-6] INFO com.iluwatar.backpressure.Subscriber -- process(6)
+23:10:00.223 [parallel-7] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(7)
+23:10:00.729 [parallel-7] INFO com.iluwatar.backpressure.Subscriber -- process(7)
+23:10:00.930 [parallel-8] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(8)
+23:10:01.436 [parallel-8] INFO com.iluwatar.backpressure.Subscriber -- process(8)
+23:10:01.437 [parallel-8] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onComplete()
+```
+
+## When to Use the Backpressure Pattern In Java
+
+* Use in Java systems where data is produced at high velocity and consumers risk overload.
+* Applicable in reactive or event-driven architectures to maintain stability under varying load conditions.
+
+## Benefits and Trade-offs of Backpressure Pattern
+
+Benefits:
+
+* Protects consumers from saturation and resource exhaustion.
+
+Trade-offs:
+
+* Introduces possible delays if production must slow down to match consumer capacity.
+* Requires careful orchestration in complex systems with multiple concurrent data sources.
+
+## Related Java Design Patterns
+
+* [Observer Pattern](https://java-design-patterns.com/patterns/observer/): Both patterns involve a producer notifying consumers. Observer is synchronous and tightly coupled (observers know the subject).
+* [Publish-Subscribe Pattern](https://java-design-patterns.com/patterns/publish-subscribe/): Both patterns deal with asynchronous data flow and can work together to manage message distribution and consumption effectively.
+
+## References and Credits
+
+* [Backpressure Explained (RedHat Developers Blog)](https://developers.redhat.com/articles/backpressure-explained)
+* [Hands-On Reactive Programming in Spring 5](https://amzn.to/3YuYfyO)
+* [Reactive Programming with RxJava: Creating Asynchronous, Event-Based Applications](https://amzn.to/42negbf)
+* [Reactive Streams in Java](https://amzn.to/3RJjUzA)
+* [Reactive Streams Specification](https://www.reactive-streams.org/)
diff --git a/backpressure/etc/backpressure-sequence-diagram.png b/backpressure/etc/backpressure-sequence-diagram.png
new file mode 100644
index 000000000000..b74a5d2a5362
Binary files /dev/null and b/backpressure/etc/backpressure-sequence-diagram.png differ
diff --git a/backpressure/etc/backpressure.png b/backpressure/etc/backpressure.png
new file mode 100644
index 000000000000..0de9ff54b673
Binary files /dev/null and b/backpressure/etc/backpressure.png differ
diff --git a/backpressure/pom.xml b/backpressure/pom.xml
new file mode 100644
index 000000000000..fcc15892fb8a
--- /dev/null
+++ b/backpressure/pom.xml
@@ -0,0 +1,81 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ backpressure
+
+
+
+ io.projectreactor
+ reactor-core
+ 3.8.0-M1
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ io.projectreactor
+ reactor-test
+ 3.8.0-M1
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.backpressure.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/App.java b/backpressure/src/main/java/com/iluwatar/backpressure/App.java
new file mode 100644
index 000000000000..4632c42a467f
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/App.java
@@ -0,0 +1,76 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * The Backpressure pattern is a flow control mechanism. It allows a consumer to signal to a
+ * producer to slow down or stop sending data when it's overwhelmed.
+ * Prevents memory overflow, CPU thrashing, and resource exhaustion.
+ * Ensures fair usage of resources in distributed systems.
+ * Avoids buffer bloat and latency spikes. Key concepts of this design paradigm involves
+ * Publisher/Producer: Generates data.
+ * Subscriber/Consumer: Receives and processes data.
+ *
+ * In this example we will create a {@link Publisher} and a {@link Subscriber}. Publisher
+ * will emit a stream of integer values with a predefined delay. Subscriber takes 500 ms to
+ * process one integer. Since the subscriber can't process the items fast enough we apply
+ * backpressure to the publisher so that it will request 10 items first, process 5 items and
+ * request for the next 5 again. After processing 5 items subscriber will keep requesting for
+ * another 5 until the stream ends.
+ */
+public class App {
+
+ protected static CountDownLatch latch;
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) throws InterruptedException {
+
+ /*
+ * This custom subscriber applies backpressure:
+ * - Has a processing delay of 0.5 milliseconds
+ * - Requests 10 items initially
+ * - Process 5 items and request for the next 5 items
+ */
+ Subscriber sub = new Subscriber();
+ // slow publisher emit 15 numbers with a delay of 200 milliseconds
+ Publisher.publish(1, 17, 200).subscribe(sub);
+
+ latch = new CountDownLatch(1);
+ latch.await();
+
+ sub = new Subscriber();
+ // fast publisher emit 15 numbers with a delay of 1 millisecond
+ Publisher.publish(1, 17, 1).subscribe(sub);
+
+ latch = new CountDownLatch(1);
+ latch.await();
+ }
+}
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java b/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java
new file mode 100644
index 000000000000..1d39a070ae57
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import java.time.Duration;
+import reactor.core.publisher.Flux;
+
+/** This class is the publisher that generates the data stream. */
+public class Publisher {
+
+ private Publisher() {}
+
+ /**
+ * On message method will trigger when the subscribed event is published.
+ *
+ * @param start starting integer
+ * @param count how many integers to emit
+ * @param delay delay between each item in milliseconds
+ * @return a flux stream of integers
+ */
+ public static Flux publish(int start, int count, int delay) {
+ return Flux.range(start, count).delayElements(Duration.ofMillis(delay)).log();
+ }
+}
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java b/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java
new file mode 100644
index 000000000000..40ff5aebc814
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Subscription;
+import reactor.core.publisher.BaseSubscriber;
+
+/** This class is the custom subscriber that subscribes to the data stream. */
+@Slf4j
+public class Subscriber extends BaseSubscriber {
+
+ @Override
+ protected void hookOnSubscribe(@NonNull Subscription subscription) {
+ request(10); // request 10 items initially
+ }
+
+ @Override
+ protected void hookOnNext(@NonNull Integer value) {
+ processItem();
+ LOGGER.info("process({})", value);
+ if (value % 5 == 0) {
+ // request for the next 5 items after processing first 5
+ request(5);
+ }
+ }
+
+ @Override
+ protected void hookOnComplete() {
+ App.latch.countDown();
+ }
+
+ private void processItem() {
+ try {
+ Thread.sleep(500); // simulate slow processing
+ } catch (InterruptedException e) {
+ LOGGER.error(e.getMessage(), e);
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java
new file mode 100644
index 000000000000..8fe2245a97b7
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+public class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java b/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java
new file mode 100644
index 000000000000..e99926e00a1a
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java
@@ -0,0 +1,60 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.slf4j.LoggerFactory;
+
+public class LoggerExtension implements BeforeEachCallback, AfterEachCallback {
+
+ private final ListAppender listAppender = new ListAppender<>();
+ private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+
+ @Override
+ public void afterEach(ExtensionContext extensionContext) {
+ listAppender.stop();
+ listAppender.list.clear();
+ logger.detachAppender(listAppender);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext extensionContext) {
+ logger.addAppender(listAppender);
+ listAppender.start();
+ }
+
+ public List getFormattedMessages() {
+ return listAppender.list.stream()
+ .map(ILoggingEvent::getFormattedMessage)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java
new file mode 100644
index 000000000000..010a80e83959
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import static com.iluwatar.backpressure.Publisher.publish;
+
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Flux;
+import reactor.test.StepVerifier;
+
+public class PublisherTest {
+
+ @Test
+ public void testPublish() {
+
+ Flux flux = publish(1, 3, 200);
+
+ StepVerifier.withVirtualTime(() -> flux)
+ .expectSubscription()
+ .expectNoEvent(Duration.ofMillis(200))
+ .expectNext(1)
+ .expectNoEvent(Duration.ofSeconds(200))
+ .expectNext(2)
+ .expectNoEvent(Duration.ofSeconds(200))
+ .expectNext(3)
+ .verifyComplete();
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java
new file mode 100644
index 000000000000..7f7676612d02
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java
@@ -0,0 +1,54 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.concurrent.CountDownLatch;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class SubscriberTest {
+
+ @RegisterExtension public LoggerExtension loggerExtension = new LoggerExtension();
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+
+ Subscriber sub = new Subscriber();
+ Publisher.publish(1, 8, 100).subscribe(sub);
+
+ App.latch = new CountDownLatch(1);
+ App.latch.await();
+
+ String result = String.join(",", loggerExtension.getFormattedMessages());
+ assertTrue(
+ result.endsWith(
+ "onSubscribe(FluxConcatMapNoPrefetch."
+ + "FluxConcatMapNoPrefetchSubscriber),request(10),onNext(1),process(1),onNext(2),"
+ + "process(2),onNext(3),process(3),onNext(4),process(4),onNext(5),process(5),request(5),"
+ + "onNext(6),process(6),onNext(7),process(7),onNext(8),process(8),onComplete()"));
+ }
+}
diff --git a/balking/README.md b/balking/README.md
index fb5c83d5b412..cdd09cf5b7d1 100644
--- a/balking/README.md
+++ b/balking/README.md
@@ -1,27 +1,161 @@
---
-layout: pattern
-title: Balking
-folder: balking
-permalink: /patterns/balking/
-categories: Concurrency
-tags:
- - Java
- - Difficulty-Beginner
+title: "Balking Pattern in Java: Smart Control Over Java Execution"
+shortTitle: Balking
+description: "Learn the Balking design pattern in Java, a concurrency pattern that prevents code execution in inappropriate states. Discover examples, use cases, and benefits."
+category: Concurrency
+language: en
+tag:
+ - Concurrency
+ - Decoupling
+ - Fault tolerance
+ - Synchronization
---
-## Intent
-Balking Pattern is used to prevent an object from executing certain code if it is an
-incomplete or inappropriate state
+## Intent of Balking Design Pattern
-
+The Balking Pattern in Java is a concurrency design pattern that prevents an object from executing certain code if it is in an incomplete or inappropriate state. This pattern is crucial for managing state and concurrency in multithreaded Java applications.
+
+## Detailed Explanation of Balking Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy of the Balking design pattern can be seen in a laundry service. Imagine a washing machine at a laundromat that only starts washing clothes if the door is properly closed and locked. If a user tries to start the machine while the door is open, the machine balks and does nothing. This ensures that the washing process only begins when it is safe to do so, preventing water spillage and potential damage to the machine. Similarly, the Balking pattern in software design ensures that operations are only executed when the object is in an appropriate state, preventing erroneous actions and maintaining system stability.
+
+In plain words
+
+> Using the balking pattern, a certain code executes only if the object is in particular state.
+
+Wikipedia says
+
+> The balking pattern is a software design pattern that only executes an action on an object when the object is in a particular state. For example, if an object reads ZIP files and a calling method invokes a get method on the object when the ZIP file is not open, the object would "balk" at the request.
+
+Flowchart
+
+
+
+## Programmatic Example of Balking Pattern in Java
+
+This example demonstrates the Balking Pattern in a multithreaded Java application, highlighting state management and concurrency control. The Balking Pattern is exemplified by a washing machine's start button that initiates washing only if the machine is idle. This ensures state management and prevents concurrent issues.
+
+There's a start-button in a washing machine to initiate the laundry washing. When the washing machine is inactive the button works as expected, but if it's already washing the button does nothing.
+
+In this example implementation, `WashingMachine` is an object that has two states in which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe method. On the other hand, if it already has been washing and any other thread executes `wash`it won't do that and returns without doing anything.
+
+Here are the relevant parts of the `WashingMachine` class.
+
+```java
+
+@Slf4j
+public class WashingMachine {
+
+ private final DelayProvider delayProvider;
+ private WashingMachineState washingMachineState;
+
+ public WashingMachine(DelayProvider delayProvider) {
+ this.delayProvider = delayProvider;
+ this.washingMachineState = WashingMachineState.ENABLED;
+ }
+
+ public WashingMachineState getWashingMachineState() {
+ return washingMachineState;
+ }
+
+ public void wash() {
+ synchronized (this) {
+ var machineState = getWashingMachineState();
+ LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
+ if (this.washingMachineState == WashingMachineState.WASHING) {
+ LOGGER.error("Cannot wash if the machine has been already washing!");
+ return;
+ }
+ this.washingMachineState = WashingMachineState.WASHING;
+ }
+ LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
+ this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
+ }
+
+ public synchronized void endOfWashing() {
+ washingMachineState = WashingMachineState.ENABLED;
+ LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
+ }
+}
+```
+
+Here's the simple `DelayProvider` interface used by the `WashingMachine`.
+
+```java
+public interface DelayProvider {
+ void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
+}
+```
+
+Now, we introduce the application using the `WashingMachine`.
+
+```java
+public static void main(String... args) {
+ final var washingMachine = new WashingMachine();
+ var executorService = Executors.newFixedThreadPool(3);
+ for (int i = 0; i < 3; i++) {
+ executorService.execute(washingMachine::wash);
+ }
+ executorService.shutdown();
+ try {
+ if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ }
+ } catch (InterruptedException ie) {
+ LOGGER.error("ERROR: Waiting on executor service shutdown!");
+ Thread.currentThread().interrupt();
+ }
+}
+```
+
+Here is the console output of the program.
+
+```
+14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED
+14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing
+14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING
+14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
+14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING
+14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
+14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
+```
+
+## When to Use the Balking Pattern in Java
-## Applicability
Use the Balking pattern when
-* you want to invoke an action on an object only when it is in a particular state
-* objects are generally only in a state that is prone to balking temporarily
-but for an unknown amount of time
+* You want to invoke an action on an object only when it is in a particular state
+* Objects are generally only in a state that is prone to balking temporarily but for an unknown amount of time
+* In multithreaded applications where certain actions should only proceed when specific conditions are met, and those conditions are expected to change over time due to external factors or concurrent operations.
+
+## Real-World Applications of Balking Pattern in Java
+
+* Resource pooling, where resources are only allocated if they are in a valid state for allocation.
+* Thread management, where threads only proceed with tasks if certain conditions (like task availability or resource locks) are met.
+
+## Benefits and Trade-offs of Balking Pattern
+
+Benefits:
+
+* Reduces unnecessary lock acquisitions in situations where actions cannot proceed, enhancing performance in concurrent applications.
+* Encourages clear separation of state management and behavior, leading to cleaner code.
+* Simplifies the handling of operations that should only be performed under certain conditions without cluttering the caller code with state checks.
+
+Trade-offs:
+
+* Can introduce complexity by obscuring the conditions under which actions are taken or ignored, potentially making the system harder to debug and understand.
+* May lead to missed opportunities or actions if the state changes are not properly monitored or if the balking condition is too restrictive.
+
+## Related Java Design Patterns
+
+* [Double-Checked Locking](https://java-design-patterns.com/patterns/double-checked-locking/): Ensures that initialization occurs only when necessary and avoids unnecessary locking, which is related to Balking in terms of conditionally executing logic based on the object's state.
+* [Guarded Suspension](https://java-design-patterns.com/patterns/guarded-suspension/): Similar in ensuring actions are only performed when an object is in a certain state, but typically involves waiting until the state is valid.
+* [State](https://java-design-patterns.com/patterns/state/): The State pattern can be used in conjunction with Balking to manage the states and transitions of the object.
+
+## References and Credits
-## Related patterns
-* Guarded Suspension Pattern
-* Double Checked Locking Pattern
+* [Concurrent Programming in Java : Design Principles and Patterns](https://amzn.to/4dIBqxL)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
diff --git a/balking/etc/balking-flowchart.png b/balking/etc/balking-flowchart.png
new file mode 100644
index 000000000000..400a1ce8d3e3
Binary files /dev/null and b/balking/etc/balking-flowchart.png differ
diff --git a/balking/etc/balking.urm.puml b/balking/etc/balking.urm.puml
index b0296b925417..191fd350bcd8 100644
--- a/balking/etc/balking.urm.puml
+++ b/balking/etc/balking.urm.puml
@@ -5,10 +5,15 @@ package com.iluwatar.balking {
+ App()
+ main(args : String[]) {static}
}
+ interface DelayProvider {
+ + executeAfterDelay(long, TimeUnit, Runnable) {abstract}
+ }
class WashingMachine {
- LOGGER : Logger {static}
+ - delayProvider : DelayProvider
- washingMachineState : WashingMachineState
+ WashingMachine()
+ + WashingMachine(delayProvider : DelayProvider)
+ endOfWashing()
+ getWashingMachineState() : WashingMachineState
+ wash()
@@ -21,4 +26,5 @@ package com.iluwatar.balking {
}
}
WashingMachine --> "-washingMachineState" WashingMachineState
+WashingMachine --> "-delayProvider" DelayProvider
@enduml
\ No newline at end of file
diff --git a/balking/pom.xml b/balking/pom.xml
index 28fa0db61a11..818f7549c330 100644
--- a/balking/pom.xml
+++ b/balking/pom.xml
@@ -1,8 +1,10 @@
-
+
java-design-patterns
com.iluwatar
- 1.18.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
4.0.0
-
balking
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
-
-
-
\ No newline at end of file
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.balking.App
+
+
+
+
+
+
+
+
+
diff --git a/balking/src/main/java/com/iluwatar/balking/App.java b/balking/src/main/java/com/iluwatar/balking/App.java
index 47b10cd4606d..c7651ec54147 100644
--- a/balking/src/main/java/com/iluwatar/balking/App.java
+++ b/balking/src/main/java/com/iluwatar/balking/App.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,44 +24,43 @@
*/
package com.iluwatar.balking;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import lombok.extern.slf4j.Slf4j;
/**
* In Balking Design Pattern if an object’s method is invoked when it is in an inappropriate state,
- * then the method will return without doing anything. Objects that use this pattern are generally only in a
- * state that is prone to balking temporarily but for an unknown amount of time
+ * then the method will return without doing anything. Objects that use this pattern are generally
+ * only in a state that is prone to balking temporarily but for an unknown amount of time
*
- * In this example implementation WashingMachine is an object that has two states
- * in which it can be: ENABLED and WASHING. If the machine is ENABLED
- * the state is changed into WASHING that any other thread can't invoke this action on this and then do the job.
- * On the other hand if it have been already washing and any other thread execute wash()
- * it can't do that once again and returns doing nothing.
+ *
In this example implementation, {@link WashingMachine} is an object that has two states in
+ * which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING
+ * using a thread-safe method. On the other hand, if it already has been washing and any other
+ * thread executes {@link WashingMachine#wash()} it won't do that and returns without doing
+ * anything.
*/
-
+@Slf4j
public class App {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
/**
+ * Entry Point.
+ *
* @param args the command line arguments - not used
*/
public static void main(String... args) {
- final WashingMachine washingMachine = new WashingMachine();
- ExecutorService executorService = Executors.newFixedThreadPool(3);
+ final var washingMachine = new WashingMachine();
+ var executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
executorService.execute(washingMachine::wash);
}
executorService.shutdown();
try {
- executorService.awaitTermination(10, TimeUnit.SECONDS);
+ if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ }
} catch (InterruptedException ie) {
LOGGER.error("ERROR: Waiting on executor service shutdown!");
+ Thread.currentThread().interrupt();
}
}
-
}
diff --git a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
new file mode 100644
index 000000000000..f27922219ea6
--- /dev/null
+++ b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
@@ -0,0 +1,32 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.balking;
+
+import java.util.concurrent.TimeUnit;
+
+/** An interface to simulate delay while executing some work. */
+public interface DelayProvider {
+ void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
+}
diff --git a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
index e4f1259add25..52ce7c593b6b 100644
--- a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,55 +24,60 @@
*/
package com.iluwatar.balking;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
-/**
- * Washing machine class
- */
+/** Washing machine class. */
+@Slf4j
public class WashingMachine {
- private static final Logger LOGGER = LoggerFactory.getLogger(WashingMachine.class);
+ private final DelayProvider delayProvider;
- private WashingMachineState washingMachineState;
+ @Getter private WashingMachineState washingMachineState;
+ /** Creates a new instance of WashingMachine. */
public WashingMachine() {
- washingMachineState = WashingMachineState.ENABLED;
- }
-
- public WashingMachineState getWashingMachineState() {
- return washingMachineState;
+ this(
+ (interval, timeUnit, task) -> {
+ try {
+ Thread.sleep(timeUnit.toMillis(interval));
+ } catch (InterruptedException ie) {
+ LOGGER.error("", ie);
+ Thread.currentThread().interrupt();
+ }
+ task.run();
+ });
}
/**
- * Method responsible for washing
- * if the object is in appropriate state
+ * Creates a new instance of WashingMachine using provided delayProvider. This constructor is used
+ * only for unit testing purposes.
*/
+ public WashingMachine(DelayProvider delayProvider) {
+ this.delayProvider = delayProvider;
+ this.washingMachineState = WashingMachineState.ENABLED;
+ }
+
+ /** Method responsible for washing if the object is in appropriate state. */
public void wash() {
synchronized (this) {
- LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), getWashingMachineState());
- if (washingMachineState == WashingMachineState.WASHING) {
- LOGGER.error("ERROR: Cannot wash if the machine has been already washing!");
+ var machineState = getWashingMachineState();
+ LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
+ if (this.washingMachineState == WashingMachineState.WASHING) {
+ LOGGER.error("Cannot wash if the machine has been already washing!");
return;
}
- washingMachineState = WashingMachineState.WASHING;
+ this.washingMachineState = WashingMachineState.WASHING;
}
LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
- try {
- Thread.sleep(50);
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- endOfWashing();
+
+ this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
}
- /**
- * Method responsible of ending the washing
- * by changing machine state
- */
+ /** Method is responsible for ending the washing by changing machine state. */
public synchronized void endOfWashing() {
washingMachineState = WashingMachineState.ENABLED;
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
}
-
}
diff --git a/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
index 40a5b2f38e30..e0b2ba7ac8f1 100644
--- a/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -23,10 +25,10 @@
package com.iluwatar.balking;
/**
- * WashingMachineState enum describes in which state machine is,
- * it can be enabled and ready to work as well as during washing
+ * WashingMachineState enum describes in which state machine is, it can be enabled and ready to work
+ * as well as during washing.
*/
-
public enum WashingMachineState {
- ENABLED, WASHING
+ ENABLED,
+ WASHING
}
diff --git a/balking/src/test/java/com/iluwatar/balking/AppTest.java b/balking/src/test/java/com/iluwatar/balking/AppTest.java
index df104b901672..40beabf553d0 100644
--- a/balking/src/test/java/com/iluwatar/balking/AppTest.java
+++ b/balking/src/test/java/com/iluwatar/balking/AppTest.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -20,20 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.balking;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
-public class AppTest {
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+/** Application test */
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
+ */
@Test
- public void main() throws Exception {
- String[] args = {};
- App.main(args);
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow((Executable) App::main);
}
-
-}
\ No newline at end of file
+}
diff --git a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
index ecf81f409e06..9bf7ac2548a0 100644
--- a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
+++ b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
@@ -1,17 +1,19 @@
-/**
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
* The MIT License
- * Copyright (c) 2014 Ilkka Seppälä
- *
+ * Copyright © 2014-2022 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
@@ -22,44 +24,47 @@
*/
package com.iluwatar.balking;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+/** Tests for {@link WashingMachine} */
+class WashingMachineTest {
-/**
- * Tests for {@link WashingMachine}
- */
-public class WashingMachineTest {
-
- private volatile WashingMachineState machineStateGlobal;
+ private final FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
@Test
- public void wash() throws Exception {
- WashingMachine washingMachine = new WashingMachine();
- ExecutorService executorService = Executors.newFixedThreadPool(2);
- executorService.execute(washingMachine::wash);
- executorService.execute(() -> {
- washingMachine.wash();
- machineStateGlobal = washingMachine.getWashingMachineState();
- });
- executorService.shutdown();
- try {
- executorService.awaitTermination(10, TimeUnit.SECONDS);
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
+ void wash() {
+ var washingMachine = new WashingMachine(fakeDelayProvider);
+
+ washingMachine.wash();
+ washingMachine.wash();
+
+ var machineStateGlobal = washingMachine.getWashingMachineState();
+
+ fakeDelayProvider.task.run();
+
+ // washing machine remains in washing state
assertEquals(WashingMachineState.WASHING, machineStateGlobal);
+
+ // washing machine goes back to enabled state
+ assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
}
@Test
- public void endOfWashing() throws Exception {
- WashingMachine washingMachine = new WashingMachine();
+ void endOfWashing() {
+ var washingMachine = new WashingMachine();
washingMachine.wash();
assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
}
-}
\ No newline at end of file
+ private static class FakeDelayProvider implements DelayProvider {
+ private Runnable task;
+
+ @Override
+ public void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task) {
+ this.task = task;
+ }
+ }
+}
diff --git a/bloc/README.md b/bloc/README.md
new file mode 100644
index 000000000000..76a4a0917485
--- /dev/null
+++ b/bloc/README.md
@@ -0,0 +1,230 @@
+---
+title: "Bloc Pattern in Java: State Management Simplified"
+shortTitle: Bloc
+description: "Learn how the Bloc pattern helps manage state changes in Java applications. This guide covers dynamic listener management, real-world examples, and clean code practices for state management."
+category: Architectural
+language: en
+tag:
+ - Abstraction
+ - Data binding
+ - Decoupling
+ - Event-driven
+ - Presentation
+ - Reactive
+ - Reusability
+ - State tracking
+---
+
+## Also known as
+
+* Business Logic Component
+* Business Logic Controller
+
+## Intent of the Bloc Pattern
+
+The Bloc pattern manages the state of an object and allows for dynamically notifying interested listeners about state changes. It separates state management logic from the rest of the application, improving code organization and flexibility.
+
+## Detailed explanation of the Bloc pattern with real-World examples
+
+Real-world example
+
+> Consider a digital counter application where multiple parts of the UI need to be updated whenever the counter changes. For example, a label displaying the counter value and an activity log showing changes. Instead of directly modifying these UI components, the Bloc pattern manages the counter state and notifies all registered listeners about the state change. Listeners can dynamically subscribe or unsubscribe from receiving updates.
+
+In plain words
+
+> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes.
+
+Wikipedia says
+
+> While not a formalized "Gang of Four" design pattern, Bloc is widely used in state-driven applications. It centralizes state management and propagates state changes to registered observers, following principles of separation of concerns.
+
+Sequence diagram
+
+
+
+## Programmatic Example of the Bloc Pattern in Java
+
+This example demonstrates how to implement the Bloc pattern using Java and Swing. The pattern separates the state of the application from UI components, and provides a reactive, event-driven approach to managing updates.
+
+Core components of the Bloc Pattern include a `State` object, a `Bloc` class responsible for managing and updating that state, and interfaces (`StateListener` and `ListenerManager`) for subscribing to changes.
+
+The `State` class represents the application's data at any given time.
+
+```java
+public record State(int value) {}
+```
+
+The `ListenerManager` interface declares methods to add and remove listeners, as well as retrieve them.
+
+```java
+public interface ListenerManager {
+ void addListener(StateListener listener);
+ void removeListener(StateListener listener);
+ List> getListeners();
+}
+```
+
+The `StateListener` interface defines how a listener reacts to state changes.
+
+```java
+public interface StateListener {
+ void onStateChange(T state);
+}
+```
+
+The `Bloc` class maintains the current state and notifies listeners of updates. It provides `increment` and `decrement` methods to update state and automatically notify registered listeners.
+
+```java
+public class Bloc implements ListenerManager {
+
+ private State currentState;
+ private final List> listeners = new ArrayList<>();
+
+ public Bloc() {
+ this.currentState = new State(0);
+ }
+
+ @Override
+ public void addListener(StateListener listener) {
+ listeners.add(listener);
+ listener.onStateChange(currentState);
+ }
+
+ @Override
+ public void removeListener(StateListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public List> getListeners() {
+ return Collections.unmodifiableList(listeners);
+ }
+
+ private void emitState(State newState) {
+ currentState = newState;
+ for (StateListener listener : listeners) {
+ listener.onStateChange(currentState);
+ }
+ }
+
+ public void increment() {
+ emitState(new State(currentState.value() + 1));
+ }
+
+ public void decrement() {
+ emitState(new State(currentState.value() - 1));
+ }
+}
+```
+
+This class demonstrates how to integrate the Bloc pattern with a simple Swing GUI. It sets up a counter, buttons to change the state, and a toggle to enable or disable the listener dynamically.
+
+```java
+public class Main {
+ public static void main(String[] args) {
+ BlocUi blocUi = new BlocUi();
+ blocUi.createAndShowUi();
+ }
+}
+
+public class BlocUi {
+
+ public void createAndShowUi() {
+ final Bloc bloc = new Bloc();
+
+ JFrame frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ JButton decrementButton = new JButton("Decrement");
+ JButton toggleListenerButton = new JButton("Disable Listener");
+ JButton incrementButton = new JButton("Increment");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value());
+
+ bloc.addListener(stateListener);
+
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+
+ frame.setVisible(true);
+ }
+}
+```
+
+### Program Output
+
+- **On Increment**
+ `Counter: 1`
+
+- **On Decrement**
+ `Counter: 0`
+
+- **Dynamic Listener Toggle**
+ - Listener disabled: Counter stops updating.
+ - Listener enabled: Counter updates again.
+
+## When to Use the Bloc Pattern
+
+Use the Bloc pattern when:
+
+* When you want a clean separation of business logic and UI in Java applications
+* When you need a reactive approach to updating UI based on state changes
+* When you want to avoid coupling controllers or presenters directly to data manipulation
+* When multiple UI elements need access to the same business logic
+
+## Real-World Applications of Bloc Pattern
+
+* Java-based desktop applications that require real-time UI updates
+* Backend-driven Java frameworks that separate service layers from presentation
+* Cross-platform applications where the logic must remain consistent regardless of the UI technology
+
+## Benefits and Trade-offs of Bloc Pattern
+
+Benefits:
+
+* Simplifies UI components by removing direct business logic
+* Improves testability by isolating state and behavior
+* Encourages code reuse by centralizing data flows
+* Enhances maintainability through clear separation of concerns
+
+Trade-offs:
+
+* May introduce additional boilerplate code for managing streams or observers
+* Requires careful design to avoid a monolithic “god” component
+* Demands consistent reactive programming practices to be effective
+
+## Related Patterns
+
+- [Observer](https://java-design-patterns.com/patterns/observer/): Bloc is a specialized implementation of the Observer pattern.
+- [Mediator](https://java-design-patterns.com/patterns/mediator/): Orchestrates interactions among multiple objects through a central component
+- [MVC](https://java-design-patterns.com/patterns/model-view-controller/): Shares the idea of separating concerns between layers
+
+## References and Credits
+
+* [Bloc architecture(bloclibrary.dev)](https://bloclibrary.dev/architecture/)
+* [Clean Architecture: A Craftsman's Guide to Software Structure and Design](https://amzn.to/3UoKkaR)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Event-Driven Programming in Java (Oracle)](https://www.oracle.com/java/)
+* [Java Swing Documentation (Oracle)](https://docs.oracle.com/javase/tutorial/uiswing/)
diff --git a/bloc/etc/bloc-sequence-diagram.png b/bloc/etc/bloc-sequence-diagram.png
new file mode 100644
index 000000000000..588863985019
Binary files /dev/null and b/bloc/etc/bloc-sequence-diagram.png differ
diff --git a/bloc/etc/bloc.png b/bloc/etc/bloc.png
new file mode 100644
index 000000000000..60d6eb77c8fc
Binary files /dev/null and b/bloc/etc/bloc.png differ
diff --git a/bloc/etc/bloc.puml b/bloc/etc/bloc.puml
new file mode 100644
index 000000000000..5991f533ae70
--- /dev/null
+++ b/bloc/etc/bloc.puml
@@ -0,0 +1,41 @@
+@startuml
+package com.iluwatar.bloc {
+
+ class State {
+ - value : int
+ + State(value : int)
+ + getValue() : int
+ }
+
+ interface StateListener {
+ + onStateChange(state : T)
+ }
+
+ interface ListenerManager {
+ + addListener(listener : StateListener)
+ + removeListener(listener : StateListener)
+ + getListeners() : List>
+ }
+
+ class BloC {
+ - currentState : State
+ - listeners : List>
+ + BloC()
+ + addListener(listener : StateListener)
+ + removeListener(listener : StateListener)
+ + getListeners() : List>
+ - emitState(newState : State)
+ + increment()
+ + decrement()
+ }
+
+ class Main {
+ + main(args : String[])
+ }
+
+ ListenerManager <|.. BloC
+ StateListener <|.. BloC
+ BloC o-- State
+ BloC *-- StateListener
+}
+@enduml
diff --git a/bloc/etc/bloc.urm.puml b/bloc/etc/bloc.urm.puml
new file mode 100644
index 000000000000..6408aa76e6a4
--- /dev/null
+++ b/bloc/etc/bloc.urm.puml
@@ -0,0 +1,32 @@
+@startuml
+package com.iluwatar.bloc {
+ class Bloc {
+ - currentState : State
+ - listeners : List>
+ + Bloc()
+ + addListener(listener : StateListener)
+ + decrement()
+ - emitState(newState : State)
+ + getListeners() : List>
+ + increment()
+ + removeListener(listener : StateListener)
+ }
+ class BlocUi {
+ + BlocUi()
+ + createAndShowUi()
+ }
+ interface ListenerManager {
+ + addListener(StateListener) {abstract}
+ + getListeners() : List> {abstract}
+ + removeListener(StateListener) {abstract}
+ }
+ class Main {
+ + Main()
+ + main(args : String[]) {static}
+ }
+ interface StateListener {
+ + onStateChange(T) {abstract}
+ }
+}
+Bloc ..|> ListenerManager
+@enduml
\ No newline at end of file
diff --git a/bloc/pom.xml b/bloc/pom.xml
new file mode 100644
index 000000000000..cc52a3b99dc2
--- /dev/null
+++ b/bloc/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ bloc
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.testng
+ testng
+ 7.11.0
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.27.3
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+