From 88c3b04eb38441867ed4de7fb71048d0bbc52da9 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sun, 7 Feb 2016 17:05:33 -0600 Subject: [PATCH 001/207] #297 Create API Gateway pattern --- api-gateway/pom.xml | 19 ++++++++ .../com/iluwatar/api/gateway/ApiGateway.java | 31 +++++++++++++ .../java/com/iluwatar/api/gateway/App.java | 46 +++++++++++++++++++ .../iluwatar/api/gateway/DesktopProduct.java | 26 +++++++++++ .../iluwatar/api/gateway/ImageService.java | 15 ++++++ .../iluwatar/api/gateway/MobileProduct.java | 17 +++++++ .../iluwatar/api/gateway/PriceService.java | 15 ++++++ pom.xml | 1 + 8 files changed, 170 insertions(+) create mode 100644 api-gateway/pom.xml create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/App.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/ImageService.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml new file mode 100644 index 000000000000..bb7f5701da0d --- /dev/null +++ b/api-gateway/pom.xml @@ -0,0 +1,19 @@ + + + + java-design-patterns + com.iluwatar + 1.10.0-SNAPSHOT + + 4.0.0 + api-gateway + + + junit + junit + test + + + \ No newline at end of file diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java new file mode 100644 index 000000000000..0ecbf7405e27 --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java @@ -0,0 +1,31 @@ +package com.iluwatar.api.gateway; + +/** + * The ApiGateway aggregates calls to microservices based on the needs of the individual clients. + */ +public class ApiGateway { + + private ImageService imageService = new ImageService(); + private PriceService priceService = new PriceService(); + + /** + * Retrieves product information that desktop clients need + * @return Product information for clients on a desktop + */ + public DesktopProduct getProductDesktop() { + DesktopProduct desktopProduct = new DesktopProduct(); + desktopProduct.setImagePath(imageService.getImagePath()); + desktopProduct.setPrice(priceService.getPrice()); + return desktopProduct; + } + + /** + * Retrieves product information that mobile clients need + * @return Product information for clients on a mobile device + */ + public MobileProduct getProductMobile() { + MobileProduct mobileProduct = new MobileProduct(); + mobileProduct.setPrice(priceService.getPrice()); + return mobileProduct; + } +} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java new file mode 100644 index 000000000000..7bad0269bb0b --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java @@ -0,0 +1,46 @@ +package com.iluwatar.api.gateway; + +/** + * 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. + * In this case, the (@link ImageService) and (@link PriceService) represent our microservices. + * 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). + */ +public class App { + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + ApiGateway apiGateway = new ApiGateway(); + + DesktopProduct desktopProduct = apiGateway.getProductDesktop(); + System.out.println(String.format("Desktop Product \nPrice: %s\nImage path: %s", + desktopProduct.getPrice(), desktopProduct.getImagePath())); + + MobileProduct mobileProduct = apiGateway.getProductMobile(); + System.out.println(String.format("Mobile Product \nPrice: %s", mobileProduct.getPrice())); + } +} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java new file mode 100644 index 000000000000..8105b01b1d2b --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java @@ -0,0 +1,26 @@ +package com.iluwatar.api.gateway; + +/** + * Encapsulates all of the information that a desktop client needs to display a product. + */ +public class DesktopProduct { + + private String price; + 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/src/main/java/com/iluwatar/api/gateway/ImageService.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageService.java new file mode 100644 index 000000000000..3904f545dc92 --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageService.java @@ -0,0 +1,15 @@ +package com.iluwatar.api.gateway; + +/** + * Represents a microservice used to retrieve image data. + */ +public class ImageService { + + /** + * Gets the image path + * @return the image path + */ + public String getImagePath() { + return "/product-image.png"; + } +} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java new file mode 100644 index 000000000000..772e540df64f --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java @@ -0,0 +1,17 @@ +package com.iluwatar.api.gateway; + +/** + * Encapsulates all of the information that mobile client needs to display a product. + */ +public class MobileProduct { + + private String price; + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } +} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java new file mode 100644 index 000000000000..f64f1cb11ed3 --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java @@ -0,0 +1,15 @@ +package com.iluwatar.api.gateway; + +/** + * Represents a microservice used to retrieve price data. + */ +public class PriceService { + + /** + * Gets the price + * @return the price + */ + public String getPrice() { + return "20"; + } +} diff --git a/pom.xml b/pom.xml index d2b02fdf79f7..732193893c92 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ publish-subscribe delegation event-driven-architecture + api-gateway From 3cbb88e09cd145f3e04b0a227982e158e1b02254 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sun, 7 Feb 2016 17:05:45 -0600 Subject: [PATCH 002/207] #297 Create unit tests for Gateway API pattern --- .../iluwatar/api/gateway/ApiGatewayTest.java | 31 +++++++++++++++++++ .../com/iluwatar/api/gateway/AppTest.java | 17 ++++++++++ 2 files changed, 48 insertions(+) create mode 100644 api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java create mode 100644 api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java diff --git a/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java new file mode 100644 index 000000000000..00abdacb52a7 --- /dev/null +++ b/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java @@ -0,0 +1,31 @@ +package com.iluwatar.api.gateway; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ApiGatewayTest { + + /** + * Tests getting the data for a desktop client + */ + @Test + public void testGetProductDesktop() { + ApiGateway apiGateway = new ApiGateway(); + DesktopProduct desktopProduct = apiGateway.getProductDesktop(); + + assertEquals("20", desktopProduct.getPrice()); + assertEquals("/product-image.png", desktopProduct.getImagePath()); + } + + /** + * Tests getting the data for a mobile client + */ + @Test + public void testGetProductMobile() { + ApiGateway apiGateway = new ApiGateway(); + MobileProduct mobileProduct = apiGateway.getProductMobile(); + + assertEquals("20", mobileProduct.getPrice()); + } +} diff --git a/api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java b/api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java new file mode 100644 index 000000000000..706f34b71d9a --- /dev/null +++ b/api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.api.gateway; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} From 3ed3bc1fa52b7c1ce9a5684ce08ac03073e7668b Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Mon, 7 Mar 2016 19:40:50 +0000 Subject: [PATCH 003/207] Added mutex and semaphore modules to demonstrate locks Added two modules to demonstrate locks. Mutex demonstrates a simple mutual exclusion lock. Semaphore demonstrates a semaphore for controlling access to a pool of resources. The main class of both programs is App.java. --- mutex/pom.xml | 35 ++++++ .../src/main/java/com/iluwatar/mutex/App.java | 42 +++++++ .../src/main/java/com/iluwatar/mutex/Jar.java | 58 ++++++++++ .../main/java/com/iluwatar/mutex/Lock.java | 34 ++++++ .../main/java/com/iluwatar/mutex/Mutex.java | 46 ++++++++ .../main/java/com/iluwatar/mutex/Thief.java | 51 +++++++++ pom.xml | 2 + semaphore/pom.xml | 35 ++++++ .../main/java/com/iluwatar/semaphore/App.java | 43 ++++++++ .../java/com/iluwatar/semaphore/Customer.java | 63 +++++++++++ .../java/com/iluwatar/semaphore/Fruit.java | 60 ++++++++++ .../com/iluwatar/semaphore/FruitBowl.java | 78 +++++++++++++ .../com/iluwatar/semaphore/FruitShop.java | 103 ++++++++++++++++++ .../java/com/iluwatar/semaphore/Lock.java | 34 ++++++ .../com/iluwatar/semaphore/Semaphore.java | 51 +++++++++ 15 files changed, 735 insertions(+) create mode 100644 mutex/pom.xml create mode 100644 mutex/src/main/java/com/iluwatar/mutex/App.java create mode 100644 mutex/src/main/java/com/iluwatar/mutex/Jar.java create mode 100644 mutex/src/main/java/com/iluwatar/mutex/Lock.java create mode 100644 mutex/src/main/java/com/iluwatar/mutex/Mutex.java create mode 100644 mutex/src/main/java/com/iluwatar/mutex/Thief.java create mode 100644 semaphore/pom.xml create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/App.java create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/Customer.java create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/Lock.java create mode 100644 semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java diff --git a/mutex/pom.xml b/mutex/pom.xml new file mode 100644 index 000000000000..5b7058083d7c --- /dev/null +++ b/mutex/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + mutex + diff --git a/mutex/src/main/java/com/iluwatar/mutex/App.java b/mutex/src/main/java/com/iluwatar/mutex/App.java new file mode 100644 index 000000000000..e2d10763faa2 --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/App.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * App. + */ +public class App { + + /** + * main method + */ + public static void main(String[] args) { + Mutex mutex = new Mutex(); + Jar jar = new Jar(mutex); + Thief peter = new Thief("Peter", jar); + Thief john = new Thief("John", jar); + peter.start(); + john.start(); + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Jar.java b/mutex/src/main/java/com/iluwatar/mutex/Jar.java new file mode 100644 index 000000000000..6cbe009d88ab --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Jar.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Jar. + */ +public class Jar { + + private Lock lock; + + private int beans = 1000; + + public Jar(Lock lock) { + this.lock = lock; + } + + /** + * takeBean method + */ + public boolean takeBean(Thief thief) { + boolean success = false; + try { + lock.acquire(); + success = beans > 0; + if (success) { + beans = beans - 1; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + lock.release(); + } + + return success; + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Lock.java b/mutex/src/main/java/com/iluwatar/mutex/Lock.java new file mode 100644 index 000000000000..b748f3761475 --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Lock.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Lock. + */ +public interface Lock { + + void acquire() throws InterruptedException; + + void release(); + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java new file mode 100644 index 000000000000..a8cf6a91909b --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Mutex. + */ +public class Mutex implements Lock { + private Object owner = null; + + @Override + public synchronized void acquire() throws InterruptedException { + while (owner != null) { + wait(); + } + + owner = Thread.currentThread(); + } + + @Override + public synchronized void release() { + owner = null; + notify(); + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Thief.java b/mutex/src/main/java/com/iluwatar/mutex/Thief.java new file mode 100644 index 000000000000..8fbc75f9285c --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Thief.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Thief. + */ +public class Thief extends Thread { + + private String name; + + private Jar jar; + + public Thief(String name, Jar jar) { + this.name = name; + this.jar = jar; + } + + @Override + public void run() { + int beans = 0; + + while (jar.takeBean(this)) { + beans = beans + 1; + System.out.println(name + " took a bean."); + } + + System.out.println(name + " took " + beans + " beans."); + } + +} diff --git a/pom.xml b/pom.xml index 5558446601c8..fa0fe771cc24 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,8 @@ feature-toggle value-object monad + mutex + semaphore diff --git a/semaphore/pom.xml b/semaphore/pom.xml new file mode 100644 index 000000000000..66a961c3f001 --- /dev/null +++ b/semaphore/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + semaphore + diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/App.java b/semaphore/src/main/java/com/iluwatar/semaphore/App.java new file mode 100644 index 000000000000..4dd5a764ab1c --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/App.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * App. + */ +public class App { + + /** + * main method + */ + public static void main(String[] args) { + FruitShop shop = new FruitShop(); + new Customer("Peter", shop).start(); + new Customer("Paul", shop).start(); + new Customer("Mary", shop).start(); + new Customer("John", shop).start(); + new Customer("Ringo", shop).start(); + new Customer("George", shop).start(); + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java new file mode 100644 index 000000000000..a3fc65ac3ff4 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Customer. + */ +public class Customer extends Thread { + + private String name; + private FruitShop fruitShop; + private FruitBowl fruitBowl; + + /** + * Customer constructor + */ + public Customer(String name, FruitShop fruitShop) { + this.name = name; + this.fruitShop = fruitShop; + this.fruitBowl = new FruitBowl(); + } + + /** + * run method + */ + public void run() { + + while (fruitShop.countFruit() > 0) { + FruitBowl bowl = fruitShop.takeBowl(); + Fruit fruit; + + if (bowl != null && (fruit = bowl.take()) != null) { + System.out.println(name + " took an " + fruit); + fruitBowl.put(fruit); + fruitShop.returnBowl(bowl); + } + } + + System.out.println(name + " took " + fruitBowl); + + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java b/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java new file mode 100644 index 000000000000..ecf8d1d29f5a --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Fruit. + */ +public class Fruit { + + public static enum FruitType { + ORANGE, APPLE, LEMON + } + + private FruitType type; + + public Fruit(FruitType type) { + this.type = type; + } + + public FruitType getType() { + return type; + } + + /** + * toString method + */ + public String toString() { + switch (type) { + case ORANGE: + return "Orange"; + case APPLE: + return "Apple"; + case LEMON: + return "Lemon"; + default: + return ""; + } + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java b/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java new file mode 100644 index 000000000000..7760b8e3abff --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import java.util.ArrayList; + +/** + * FruitBowl. + */ +public class FruitBowl { + + private ArrayList fruit = new ArrayList<>(); + + public int countFruit() { + return fruit.size(); + } + + public void put(Fruit f) { + fruit.add(f); + } + + /** + * take method + */ + public Fruit take() { + if (fruit.isEmpty()) { + return null; + } else { + return fruit.remove(0); + } + } + + /** + * toString method + */ + public String toString() { + int apples = 0; + int oranges = 0; + int lemons = 0; + + for (Fruit f : fruit) { + switch (f.getType()) { + case APPLE: + apples++; + break; + case ORANGE: + oranges++; + break; + case LEMON: + lemons++; + break; + default: + } + } + + return apples + " Apples, " + oranges + " Oranges, and " + lemons + " Lemons"; + } +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java b/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java new file mode 100644 index 000000000000..fff764badeab --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java @@ -0,0 +1,103 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * FruitShop. + */ +public class FruitShop { + + private FruitBowl[] bowls = { + new FruitBowl(), + new FruitBowl(), + new FruitBowl() + }; + + private boolean[] available = { + true, + true, + true + }; + + private Semaphore semaphore; + + /** + * FruitShop constructor + */ + public FruitShop() { + for (int i = 0; i < 100; i++) { + bowls[0].put(new Fruit(Fruit.FruitType.APPLE)); + bowls[1].put(new Fruit(Fruit.FruitType.ORANGE)); + bowls[2].put(new Fruit(Fruit.FruitType.LEMON)); + } + + semaphore = new Semaphore(3); + } + + public synchronized int countFruit() { + return bowls[0].countFruit() + bowls[1].countFruit() + bowls[2].countFruit(); + } + + /** + * takeBowl method + */ + public synchronized FruitBowl takeBowl() { + + FruitBowl bowl = null; + + try { + semaphore.acquire(); + + if (available[0]) { + bowl = bowls[0]; + available[0] = false; + } else if (available[1]) { + bowl = bowls[1]; + available[1] = false; + } else if (available[2]) { + bowl = bowls[2]; + available[2] = false; + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + semaphore.release(); + } + return bowl; + } + + /** + * returnBowl method + */ + public synchronized void returnBowl(FruitBowl bowl) { + if (bowl == bowls[0]) { + available[0] = true; + } else if (bowl == bowls[1]) { + available[1] = true; + } else if (bowl == bowls[2]) { + available [2] = true; + } + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java b/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java new file mode 100644 index 000000000000..99b8f85b5fac --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Lock. + */ +public interface Lock { + + void acquire() throws InterruptedException; + + void release(); + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java new file mode 100644 index 000000000000..5873df2b5758 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Semaphore. + */ +public class Semaphore implements Lock { + + private int counter; + + public Semaphore(int counter) { + this.counter = counter; + } + + /** + * acquire method + */ + public synchronized void acquire() throws InterruptedException { + while (counter == 0) { + wait(); + } + counter = counter - 1; + } + + public synchronized void release() { + counter = counter + 1; + notify(); + } + +} From 2ed089900d91adbfb4c03be194a5ffa6f3680966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 13 Mar 2016 17:23:15 +0200 Subject: [PATCH 004/207] Initial commit for Hexagonal Architecture pattern --- hexagonal/pom.xml | 47 +++++++++++++++++++ .../main/java/com/iluwatar/hexagonal/App.java | 33 +++++++++++++ .../java/com/iluwatar/hexagonal/AppTest.java | 41 ++++++++++++++++ pom.xml | 9 ++-- 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 hexagonal/pom.xml create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/App.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml new file mode 100644 index 000000000000..1630e0eee7e7 --- /dev/null +++ b/hexagonal/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + com.iluwatar.hexagonal + hexagonal + 1.0-SNAPSHOT + hexagonal + http://maven.apache.org + + + junit + junit + test + + + diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java new file mode 100644 index 000000000000..a0837ba4b47a --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal; + +/** + * Hello world! + * + */ +public class App { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java new file mode 100644 index 000000000000..3a2672715d96 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest { + + /** + * Rigourous Test :-) + */ + @Test + public void testApp() { + assertTrue(true); + } +} diff --git a/pom.xml b/pom.xml index 5558446601c8..6339fb72b591 100644 --- a/pom.xml +++ b/pom.xml @@ -22,9 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---> - +--> 4.0.0 com.iluwatar @@ -123,7 +121,8 @@ feature-toggle value-object monad - + hexagonal + @@ -394,4 +393,4 @@ - + \ No newline at end of file From 06546ae3cf3c1ca9c3a149c489ce4b2aff1c7e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 13 Mar 2016 17:23:31 +0200 Subject: [PATCH 005/207] Fix license headers --- .../java/com/iluwatar/factorykit/App.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Axe.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Bow.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Builder.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Spear.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Sword.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Weapon.java | 22 +++++++++++++++++ .../iluwatar/factorykit/WeaponFactory.java | 22 +++++++++++++++++ .../com/iluwatar/factorykit/WeaponType.java | 22 +++++++++++++++++ .../com/iluwatar/factorykit/app/AppTest.java | 22 +++++++++++++++++ .../factorykit/factorykit/FactoryKitTest.java | 22 +++++++++++++++++ .../src/main/java/com/iluwatar/monad/App.java | 22 +++++++++++++++++ .../src/main/java/com/iluwatar/monad/Sex.java | 22 +++++++++++++++++ .../main/java/com/iluwatar/monad/User.java | 22 +++++++++++++++++ .../java/com/iluwatar/monad/Validator.java | 22 +++++++++++++++++ .../test/java/com/iluwatar/monad/AppTest.java | 22 +++++++++++++++++ .../java/com/iluwatar/monad/MonadTest.java | 22 +++++++++++++++++ value-object/pom.xml | 24 +++++++++++++++++++ .../java/com/iluwatar/value/object/App.java | 22 +++++++++++++++++ .../com/iluwatar/value/object/HeroStat.java | 22 +++++++++++++++++ .../com/iluwatar/value/object/AppTest.java | 22 +++++++++++++++++ .../iluwatar/value/object/HeroStatTest.java | 22 +++++++++++++++++ 22 files changed, 486 insertions(+) diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java index 91d1eb06170e..f27bee170a8c 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; /** diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java index 4e1a5e554c64..826a1f9ec8ca 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; public class Axe implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java index a90f4cf2ef96..5aa952c3db36 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; public class Bow implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java index be74626f782f..1049c7b6f9d4 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; import java.util.function.Supplier; diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java index a50f54290c69..c32811e8cecc 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; public class Spear implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java index 278febaf5cbb..208cd6bbb7e9 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; public class Sword implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java index 980a2219f3d0..3d668e352cd9 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; /** diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java index e83a997c6677..80a6fd9d35b8 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; import java.util.HashMap; diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java index ac542048d72d..283f252ded3d 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; /** diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java index 9b9af2530cec..036326d973e9 100644 --- a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit.app; import com.iluwatar.factorykit.App; diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java index ea629f57dfef..c57bee3e38d7 100644 --- a/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit.factorykit; import com.iluwatar.factorykit.*; diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java index e330cfa64bfc..7b28fdcf84a5 100644 --- a/monad/src/main/java/com/iluwatar/monad/App.java +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; import java.util.Objects; diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java index 8b7e43f3ccf3..b5d094d4ba2f 100644 --- a/monad/src/main/java/com/iluwatar/monad/Sex.java +++ b/monad/src/main/java/com/iluwatar/monad/Sex.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; public enum Sex { diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java index edd29964327a..4710945264c2 100644 --- a/monad/src/main/java/com/iluwatar/monad/User.java +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; public class User { diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java index 2298a4d8e324..cc4f3602068c 100644 --- a/monad/src/main/java/com/iluwatar/monad/Validator.java +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; import java.util.ArrayList; diff --git a/monad/src/test/java/com/iluwatar/monad/AppTest.java b/monad/src/test/java/com/iluwatar/monad/AppTest.java index 5d23b41a6759..78440b468f0a 100644 --- a/monad/src/test/java/com/iluwatar/monad/AppTest.java +++ b/monad/src/test/java/com/iluwatar/monad/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; import org.junit.Test; diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java index ae78572f87f7..4ada7191def5 100644 --- a/monad/src/test/java/com/iluwatar/monad/MonadTest.java +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; diff --git a/value-object/pom.xml b/value-object/pom.xml index 1ed2adb4377c..3cbb7bb86ec9 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -1,4 +1,28 @@ + diff --git a/value-object/src/main/java/com/iluwatar/value/object/App.java b/value-object/src/main/java/com/iluwatar/value/object/App.java index 85779fcb78ac..1e943d054ce6 100644 --- a/value-object/src/main/java/com/iluwatar/value/object/App.java +++ b/value-object/src/main/java/com/iluwatar/value/object/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; /** diff --git a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java index 101837db068b..258c4d6a0397 100644 --- a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java +++ b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; /** diff --git a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java index aed3c2f20511..85ef8b84e33c 100644 --- a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java +++ b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; import org.junit.Test; diff --git a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java index 785a4d8fefb0..4a8034b0b440 100644 --- a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java +++ b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; import static org.hamcrest.CoreMatchers.is; From b0f96adeb169aa66ade1171de271382c335fbde4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 18:13:30 +0200 Subject: [PATCH 006/207] Added class for lottery numbers and unit tests for it --- .../main/java/com/iluwatar/hexagonal/App.java | 5 +- .../hexagonal/domain/LotteryNumbers.java | 152 ++++++++++++++++++ .../java/com/iluwatar/hexagonal/AppTest.java | 8 +- .../hexagonal/domain/LotteryNumbersTest.java | 66 ++++++++ 4 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index a0837ba4b47a..b32564cbcc83 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -23,11 +23,12 @@ package com.iluwatar.hexagonal; /** - * Hello world! + * + * Example application demonstrating Hexagonal Architecture * */ public class App { public static void main(String[] args) { - System.out.println("Hello World!"); + System.out.println("Running hexagonal example"); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java new file mode 100644 index 000000000000..6f54e743b1ad --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -0,0 +1,152 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.Collections; +import java.util.HashSet; +import java.util.PrimitiveIterator; +import java.util.Random; +import java.util.Set; + +/** + * + * Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must be unique and + * between 1 and 20. + * + */ +public class LotteryNumbers { + + private final Set numbers; + + public static final int MIN_NUMBER = 1; + public static final int MAX_NUMBER = 20; + public static final int NUM_NUMBERS = 4; + + /** + * Constructor. Creates random lottery numbers. + */ + private LotteryNumbers() { + numbers = new HashSet<>(); + generateRandomNumbers(); + } + + /** + * Constructor. Uses given numbers. + */ + private LotteryNumbers(Set givenNumbers) { + numbers = new HashSet<>(); + numbers.addAll(givenNumbers); + } + + /** + * @return random LotteryNumbers + */ + public static LotteryNumbers createRandom() { + return new LotteryNumbers(); + } + + /** + * @return given LotteryNumbers + */ + public static LotteryNumbers create(Set givenNumbers) { + return new LotteryNumbers(givenNumbers); + } + + /** + * @return lottery numbers + */ + public Set getNumbers() { + return Collections.unmodifiableSet(numbers); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((numbers == null) ? 0 : numbers.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryNumbers other = (LotteryNumbers) obj; + if (numbers == null) { + if (other.numbers != null) { + return false; + } + } else if (!numbers.equals(other.numbers)) { + return false; + } + return true; + } + + /** + * Generates 4 unique random numbers between 1-20 into numbers set. + */ + private void generateRandomNumbers() { + numbers.clear(); + RandomNumberGenerator generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER); + while (numbers.size() < NUM_NUMBERS) { + int num = generator.nextInt(); + if (!numbers.contains(num)) { + numbers.add(num); + } + } + } + + /** + * + * Helper class for generating random numbers. + * + */ + private static class RandomNumberGenerator { + + private PrimitiveIterator.OfInt randomIterator; + + /** + * Initialize a new random number generator that generates random numbers in the range [min, max] + * + * @param min the min value (inclusive) + * @param max the max value (inclusive) + */ + public RandomNumberGenerator(int min, int max) { + randomIterator = new Random().ints(min, max + 1).iterator(); + } + + /** + * @return a random number in the range (min, max) + */ + public int nextInt() { + return randomIterator.nextInt(); + } + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java index 3a2672715d96..8e19cca3514b 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java @@ -22,8 +22,6 @@ */ package com.iluwatar.hexagonal; -import static org.junit.Assert.assertTrue; - import org.junit.Test; /** @@ -31,11 +29,9 @@ */ public class AppTest { - /** - * Rigourous Test :-) - */ @Test public void testApp() { - assertTrue(true); + String[] args = {}; + App.main(args); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java new file mode 100644 index 000000000000..24b999daeef4 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +/** + * + * Unit tests for {@link LotteryNumbers} + * + */ +public class LotteryNumbersTest { + + private static final int NUM_RANDOM_NUMBER_ROUNDS = 1000; + + @Test + public void testGivenNumbers() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertEquals(numbers.getNumbers().size(), 4); + assertTrue(numbers.getNumbers().contains(1)); + assertTrue(numbers.getNumbers().contains(2)); + assertTrue(numbers.getNumbers().contains(3)); + assertTrue(numbers.getNumbers().contains(4)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testNumbersCantBeModified() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + numbers.getNumbers().add(5); + } + + @Test + public void testRandomNumbers() { + for (int i = 0; i < NUM_RANDOM_NUMBER_ROUNDS; i++) { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + } + } +} From cd3a5d48d85158fa0d30145da24e5916eb2cec7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 20:37:36 +0200 Subject: [PATCH 007/207] Work on lottery numbers unit tests --- .../hexagonal/domain/LotteryNumbersTest.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java index 24b999daeef4..4f2751167623 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java @@ -23,6 +23,7 @@ package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; @@ -36,8 +37,6 @@ * */ public class LotteryNumbersTest { - - private static final int NUM_RANDOM_NUMBER_ROUNDS = 1000; @Test public void testGivenNumbers() { @@ -59,8 +58,19 @@ public void testNumbersCantBeModified() { @Test public void testRandomNumbers() { - for (int i = 0; i < NUM_RANDOM_NUMBER_ROUNDS; i++) { - LotteryNumbers numbers = LotteryNumbers.createRandom(); - } + LotteryNumbers numbers = LotteryNumbers.createRandom(); + assertEquals(numbers.getNumbers().size(), LotteryNumbers.NUM_NUMBERS); + } + + @Test + public void testEquals() { + LotteryNumbers numbers1 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + LotteryNumbers numbers2 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertTrue(numbers1.equals(numbers2)); + LotteryNumbers numbers3 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(11, 12, 13, 14))); + assertFalse(numbers1.equals(numbers3)); } } From 81718eb8ae2638616b9e3d8bc3f2602525ae06ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 21:08:55 +0200 Subject: [PATCH 008/207] Added player details and unit tests --- .../hexagonal/domain/PlayerDetails.java | 96 +++++++++++++++++++ .../hexagonal/domain/PlayerDetailsTest.java | 23 +++++ 2 files changed, 119 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java new file mode 100644 index 000000000000..3a27c0ddeafa --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -0,0 +1,96 @@ +package com.iluwatar.hexagonal.domain; + +/** + * + * Immutable value object containing lottery player details. + * + */ +public class PlayerDetails { + + private final String emailAddress; + private final String bankAccountNumber; + private final String phoneNumber; + + /** + * Constructor. + */ + private PlayerDetails(String email, String bankAccount, String phone) { + emailAddress = email; + bankAccountNumber = bankAccount; + phoneNumber = phone; + } + + /** + * Factory for creating new objects. + */ + public static PlayerDetails create(String email, String bankAccount, String phone) { + return new PlayerDetails(email, bankAccount, phone); + } + + /** + * @return email + */ + public String getEmail() { + return emailAddress; + } + + /** + * @return bank account number + */ + public String getBankAccount() { + return bankAccountNumber; + } + + /** + * @return phone number + */ + public String getPhoneNumber() { + return phoneNumber; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bankAccountNumber == null) ? 0 : bankAccountNumber.hashCode()); + result = prime * result + ((emailAddress == null) ? 0 : emailAddress.hashCode()); + result = prime * result + ((phoneNumber == null) ? 0 : phoneNumber.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PlayerDetails other = (PlayerDetails) obj; + if (bankAccountNumber == null) { + if (other.bankAccountNumber != null) { + return false; + } + } else if (!bankAccountNumber.equals(other.bankAccountNumber)) { + return false; + } + if (emailAddress == null) { + if (other.emailAddress != null) { + return false; + } + } else if (!emailAddress.equals(other.emailAddress)) { + return false; + } + if (phoneNumber == null) { + if (other.phoneNumber != null) { + return false; + } + } else if (!phoneNumber.equals(other.phoneNumber)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java new file mode 100644 index 000000000000..73faa9a94e66 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -0,0 +1,23 @@ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +/** + * + * Unit tests for {@link PlayerDetails} + * + */ +public class PlayerDetailsTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details2 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + assertEquals(details1, details2); + PlayerDetails details3 = PlayerDetails.create("john@foo.bar", "16412-123439", "+34323432"); + assertFalse(details1.equals(details3)); + } +} From 035b14fef8f26b2aa68d7d885c07ce03a90a7c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 21:57:57 +0200 Subject: [PATCH 009/207] Added lottery ticket and unit tests --- .../hexagonal/domain/LotteryTicket.java | 101 ++++++++++++++++++ .../hexagonal/domain/PlayerDetails.java | 22 ++++ .../hexagonal/domain/LotteryTicketTest.java | 49 +++++++++ .../hexagonal/domain/PlayerDetailsTest.java | 22 ++++ 4 files changed, 194 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java new file mode 100644 index 000000000000..e5828cfbf7c4 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Immutable value object representing lottery ticket. + * + */ +public class LotteryTicket { + + private final PlayerDetails playerDetails; + private final LotteryNumbers lotteryNumbers; + + /** + * Constructor. + */ + private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { + playerDetails = details; + lotteryNumbers = numbers; + } + + /** + * Factory for creating lottery tickets; + */ + public static LotteryTicket create(PlayerDetails details, LotteryNumbers numbers) { + return new LotteryTicket(details, numbers); + } + + /** + * @return player details + */ + public PlayerDetails getPlayerDetails() { + return playerDetails; + } + + /** + * @return lottery numbers + */ + public LotteryNumbers getNumbers() { + return lotteryNumbers; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((lotteryNumbers == null) ? 0 : lotteryNumbers.hashCode()); + result = prime * result + ((playerDetails == null) ? 0 : playerDetails.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicket other = (LotteryTicket) obj; + if (lotteryNumbers == null) { + if (other.lotteryNumbers != null) { + return false; + } + } else if (!lotteryNumbers.equals(other.lotteryNumbers)) { + return false; + } + if (playerDetails == null) { + if (other.playerDetails != null) { + return false; + } + } else if (!playerDetails.equals(other.playerDetails)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java index 3a27c0ddeafa..2fcdb6eb342b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; /** diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java new file mode 100644 index 000000000000..e1918686a26f --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +public class LotteryTicketTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket1 = LotteryTicket.create(details1, numbers1); + PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket2 = LotteryTicket.create(details2, numbers2); + assertEquals(ticket1, ticket2); + PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); + LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); + LotteryTicket ticket3 = LotteryTicket.create(details3, numbers3); + assertFalse(ticket1.equals(ticket3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java index 73faa9a94e66..813b035a2629 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; import static org.junit.Assert.assertEquals; From 804ffc3ea78f84f40355a28122320beb438c6909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 10:49:31 +0200 Subject: [PATCH 010/207] Added lottery service interface --- .../hexagonal/domain/LotteryService.java | 35 ++++++ .../domain/LotteryTicketSubmitResult.java | 115 ++++++++++++++++++ .../domain/LotteryTicketSubmitResultTest.java | 51 ++++++++ 3 files changed, 201 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java new file mode 100644 index 000000000000..93f446d2b39b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Interface for submitting and checking lottery tickets. + * + */ +public interface LotteryService { + + LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); + + void checkTicketForPrize(LotteryTicket ticket); +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java new file mode 100644 index 000000000000..a7a6fc14856d --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java @@ -0,0 +1,115 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.EnumSet; + +/** + * + * Immutable value object for passing lottery ticket submit results. + * + */ +public class LotteryTicketSubmitResult { + + /** + * + * Indicates if the submit was successful + * + */ + public enum Result {OK, ERROR}; + + /** + * + * Indicates the fields in the lottery ticket that have errors. + * + */ + public enum Fields {EMAIL, BANK_ACCOUNT, PHONE, NUMBERS}; + + private final Result result; + + private final EnumSet fields; + + /** + * Constructor. + */ + public LotteryTicketSubmitResult(Result submitResult) { + result = submitResult; + fields = EnumSet.noneOf(Fields.class); + } + + /** + * Constructor. + */ + public LotteryTicketSubmitResult(Result submitResult, EnumSet errorFields) { + result = submitResult; + fields = EnumSet.copyOf(errorFields); + } + + /** + * @return submit result + */ + public Result getResult() { + return result; + } + + /** + * @return fields that have errors + */ + public EnumSet getErrorFields() { + return EnumSet.copyOf(fields); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fields == null) ? 0 : fields.hashCode()); + result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicketSubmitResult other = (LotteryTicketSubmitResult) obj; + if (fields == null) { + if (other.fields != null) { + return false; + } + } else if (!fields.equals(other.fields)) { + return false; + } + if (result != other.result) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java new file mode 100644 index 000000000000..697e32f691e4 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.EnumSet; + +import org.junit.Test; + +import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Fields; +import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Result; + +/** + * + * Unit tests for {@link LotteryTicketSubmitResult} + * + */ +public class LotteryTicketSubmitResultTest { + + @Test + public void testEquals() { + LotteryTicketSubmitResult submitResult1 = new LotteryTicketSubmitResult(Result.OK); + LotteryTicketSubmitResult submitResult2 = new LotteryTicketSubmitResult(Result.OK); + assertEquals(submitResult1, submitResult2); + LotteryTicketSubmitResult submitResult3 = new LotteryTicketSubmitResult( + Result.ERROR, EnumSet.of(Fields.EMAIL, Fields.BANK_ACCOUNT)); + assertFalse(submitResult1.equals(submitResult3)); + } +} From 2785f6915b9d7ee90f3de3f4b3918c3c65ff9f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 11:10:06 +0200 Subject: [PATCH 011/207] Add lottery ticket check result --- .../hexagonal/domain/LotteryService.java | 2 +- .../domain/LotteryTicketCheckResult.java | 97 +++++++++++++++++++ .../domain/LotteryTicketCheckResultTest.java | 47 +++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 93f446d2b39b..cf07d769e2af 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -31,5 +31,5 @@ public interface LotteryService { LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); - void checkTicketForPrize(LotteryTicket ticket); + LotteryTicketCheckResult checkTicketForPrize(LotteryTicket ticket); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java new file mode 100644 index 000000000000..d36dd28b70aa --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Represents lottery ticket check result. + * + */ +public class LotteryTicketCheckResult { + + public enum CheckResult {WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED}; + + private final CheckResult checkResult; + + private final int prizeAmount; + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result) { + checkResult = result; + prizeAmount = 0; + } + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result, int amount) { + checkResult = result; + prizeAmount = amount; + } + + /** + * @return check result + */ + public CheckResult getResult() { + return checkResult; + } + + /** + * @return prize amount + */ + public int getPrizeAmount() { + return prizeAmount; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((checkResult == null) ? 0 : checkResult.hashCode()); + result = prime * result + prizeAmount; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicketCheckResult other = (LotteryTicketCheckResult) obj; + if (checkResult != other.checkResult) { + return false; + } + if (prizeAmount != other.prizeAmount) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java new file mode 100644 index 000000000000..237ab85d3e5d --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; + +/** + * + * Unit tests for {@link LotteryTicketCheckResult} + * + */ +public class LotteryTicketCheckResultTest { + + @Test + public void testEquals() { + LotteryTicketCheckResult result1 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + LotteryTicketCheckResult result2 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + assertEquals(result1, result2); + LotteryTicketCheckResult result3 = new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 300000); + assertFalse(result1.equals(result3)); + } +} From 95e6a6705670a482d706dc64fef96906f5a1cc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:09:44 +0200 Subject: [PATCH 012/207] Added interface for accessing lottery tickets in database --- .../domain/LotteryTicketRepository.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java new file mode 100644 index 000000000000..99d2ab8f0d4f --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * + * Interface for accessing lottery tickets in database. + * + */ +public interface LotteryTicketRepository { + + Optional findByUuid(UUID uuid); + Optional save(LotteryTicket ticket); + List findAll(); + +} From 15d2b9dccaa0e23b1bd1720bfee7cc2b102e846d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:26:43 +0200 Subject: [PATCH 013/207] Added interface to lottery service provider's bank account --- .../hexagonal/domain/WireTransfers.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java new file mode 100644 index 000000000000..d9463f19d553 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Interface to the lottery service provider's bank account. + * + */ +public interface WireTransfers { + + int getCurrentFundsAmount(); + boolean transferFunds(int amount, String receiverBankAccountNumber); + +} From e8671e1d6ef5e4a1cbaf9d4cadb36bc383dfe32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:33:41 +0200 Subject: [PATCH 014/207] Add interface for lottery event notifications --- .../domain/LotteryNotifications.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java new file mode 100644 index 000000000000..626525ebc7b3 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Provides notifications for lottery events. + * + */ +public interface LotteryNotifications { + + void notifyTicketSubmitted(PlayerDetails details); + void notifyNoWin(PlayerDetails details); + void notifyPrize(PlayerDetails details); + +} From fde33999b7b95198ad603c64f2ee537ca6f8f7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:38:24 +0200 Subject: [PATCH 015/207] Added interface for lottery administrators --- .../domain/LotteryAdministration.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java new file mode 100644 index 000000000000..0f0336e53f95 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.List; + +/** + * + * Administrator interface for lottery service. + * + */ +public interface LotteryAdministration { + + List getAllSubmittedTickets(); + List performLottery(); + +} From 8b147c4dd9c2c7016b04aa67f8a4e74b18ac94a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 20:56:04 +0200 Subject: [PATCH 016/207] Add mock database for lottery tickets --- .../database/LotteryTicketRepositoryMock.java | 63 +++++++++++++++++++ .../domain/LotteryTicketRepository.java | 4 +- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java new file mode 100644 index 000000000000..9c1e9fb4162f --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.database; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketRepository; + +/** + * + * Mock database for lottery tickets. + * + */ +public class LotteryTicketRepositoryMock implements LotteryTicketRepository { + + private Map tickets = new HashMap<>(); + + @Override + public Optional findByUuid(UUID uuid) { + LotteryTicket ticket = tickets.get(uuid); + if (ticket == null) { + return Optional.empty(); + } else { + return Optional.of(ticket); + } + } + + @Override + public Optional save(LotteryTicket ticket) { + UUID uuid = UUID.randomUUID(); + tickets.put(uuid, ticket); + return Optional.of(uuid); + } + + @Override + public Map findAll() { + return tickets; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java index 99d2ab8f0d4f..cbd5dcf93fcc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -22,7 +22,7 @@ */ package com.iluwatar.hexagonal.domain; -import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -35,6 +35,6 @@ public interface LotteryTicketRepository { Optional findByUuid(UUID uuid); Optional save(LotteryTicket ticket); - List findAll(); + Map findAll(); } From d2f620a5e62f187823629414286f141e45b91c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 22 Mar 2016 21:27:13 +0200 Subject: [PATCH 017/207] Added tests for lottery ticket repository --- .../domain/LotteryTicketRepositoryTest.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java new file mode 100644 index 000000000000..10394f4ca263 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; + +import org.junit.Test; + +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; + +/** + * + * Tests for {@link LotteryTicketRepository} + * + */ +public class LotteryTicketRepositoryTest { + + @Test + public void testCrudOperations() { + LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + assertEquals(repository.findAll().size(), 0); + LotteryTicket ticket = createLotteryTicket(); + Optional uuid = repository.save(ticket); + assertTrue(uuid.isPresent()); + assertEquals(repository.findAll().size(), 1); + Optional optionalTicket = repository.findByUuid(uuid.get()); + assertTrue(optionalTicket.isPresent()); + } + + private LotteryTicket createLotteryTicket() { + PlayerDetails details = PlayerDetails.create("foo@bar.com", "12231-213132", "+99324554"); + LotteryNumbers numbers = LotteryNumbers.create(new HashSet<>(Arrays.asList(1, 2, 3, 4))); + return LotteryTicket.create(details, numbers); + } +} From 266b658ab5c103ec15f9c6a5c49163129387fbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 22 Mar 2016 22:45:25 +0200 Subject: [PATCH 018/207] Refactor interfaces --- .../database/LotteryTicketRepositoryMock.java | 18 +++--- .../domain/LotteryAdministration.java | 2 +- .../hexagonal/domain/LotteryService.java | 2 +- .../hexagonal/domain/LotteryServiceImpl.java | 62 +++++++++++++++++++ .../hexagonal/domain/LotteryTicketId.java | 38 ++++++++++++ .../domain/LotteryTicketRepository.java | 7 +-- .../domain/LotteryTicketRepositoryTest.java | 6 +- 7 files changed, 117 insertions(+), 18 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java index 9c1e9fb4162f..4b9ff95d9f75 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java @@ -25,9 +25,9 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.UUID; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketRepository; /** @@ -37,11 +37,11 @@ */ public class LotteryTicketRepositoryMock implements LotteryTicketRepository { - private Map tickets = new HashMap<>(); + private Map tickets = new HashMap<>(); @Override - public Optional findByUuid(UUID uuid) { - LotteryTicket ticket = tickets.get(uuid); + public Optional findById(LotteryTicketId id) { + LotteryTicket ticket = tickets.get(id); if (ticket == null) { return Optional.empty(); } else { @@ -50,14 +50,14 @@ public Optional findByUuid(UUID uuid) { } @Override - public Optional save(LotteryTicket ticket) { - UUID uuid = UUID.randomUUID(); - tickets.put(uuid, ticket); - return Optional.of(uuid); + public Optional save(LotteryTicket ticket) { + LotteryTicketId id = new LotteryTicketId(); + tickets.put(id, ticket); + return Optional.of(id); } @Override - public Map findAll() { + public Map findAll() { return tickets; } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 0f0336e53f95..a68ea73b0e42 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -32,6 +32,6 @@ public interface LotteryAdministration { List getAllSubmittedTickets(); - List performLottery(); + LotteryNumbers performLottery(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index cf07d769e2af..324b965f4c6b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -31,5 +31,5 @@ public interface LotteryService { LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); - LotteryTicketCheckResult checkTicketForPrize(LotteryTicket ticket); + LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java new file mode 100644 index 000000000000..9ef6f80079fe --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.Optional; + +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Result; + +public class LotteryServiceImpl implements LotteryService { + + private final LotteryTicketRepository repository; + + public LotteryServiceImpl() { + repository = new LotteryTicketRepositoryMock(); + } + + @Override + public LotteryTicketSubmitResult submitTicket(LotteryTicket ticket) { + Optional optional = repository.save(ticket); + Result result = Result.OK; + if (!optional.isPresent()) { + result = Result.ERROR; + } + return new LotteryTicketSubmitResult(result); + } + + @Override + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE); + } else { + return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java new file mode 100644 index 000000000000..358bdd05a14c --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.UUID; + +public class LotteryTicketId { + + private final UUID id; + + public LotteryTicketId() { + id = UUID.randomUUID(); + } + + public UUID getId() { + return id; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java index cbd5dcf93fcc..45e4a6f61600 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -24,7 +24,6 @@ import java.util.Map; import java.util.Optional; -import java.util.UUID; /** * @@ -33,8 +32,8 @@ */ public interface LotteryTicketRepository { - Optional findByUuid(UUID uuid); - Optional save(LotteryTicket ticket); - Map findAll(); + Optional findById(LotteryTicketId id); + Optional save(LotteryTicket ticket); + Map findAll(); } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java index 10394f4ca263..b4ac6691e88b 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -46,10 +46,10 @@ public void testCrudOperations() { LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); assertEquals(repository.findAll().size(), 0); LotteryTicket ticket = createLotteryTicket(); - Optional uuid = repository.save(ticket); - assertTrue(uuid.isPresent()); + Optional id = repository.save(ticket); + assertTrue(id.isPresent()); assertEquals(repository.findAll().size(), 1); - Optional optionalTicket = repository.findByUuid(uuid.get()); + Optional optionalTicket = repository.findById(id.get()); assertTrue(optionalTicket.isPresent()); } From 6e4b2699398231f47b0bcf75b22336d4d470fd27 Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Thu, 24 Mar 2016 18:13:37 +0000 Subject: [PATCH 019/207] Update according to review comments #397 - Added descriptions - Added junit tests - Added javadoc - Added index.md - Added class diagrams --- mutex/etc/mutex.png | Bin 0 -> 12737 bytes mutex/index.md | 30 ++++++++++++++++ mutex/pom.xml | 9 ++++- .../src/main/java/com/iluwatar/mutex/App.java | 10 +++++- .../src/main/java/com/iluwatar/mutex/Jar.java | 14 ++++++-- .../main/java/com/iluwatar/mutex/Lock.java | 2 +- .../main/java/com/iluwatar/mutex/Mutex.java | 16 +++++++-- .../main/java/com/iluwatar/mutex/Thief.java | 19 +++++++--- .../test/java/com/iluwatar/mutex/AppTest.java | 34 ++++++++++++++++++ semaphore/etc/semaphore.png | Bin 0 -> 30089 bytes semaphore/index.md | 33 +++++++++++++++++ semaphore/pom.xml | 9 ++++- .../main/java/com/iluwatar/semaphore/App.java | 9 ++++- .../java/com/iluwatar/semaphore/Customer.java | 25 +++++++++---- .../java/com/iluwatar/semaphore/Fruit.java | 2 +- .../com/iluwatar/semaphore/FruitBowl.java | 16 +++++++-- .../com/iluwatar/semaphore/FruitShop.java | 31 ++++++++++++---- .../java/com/iluwatar/semaphore/Lock.java | 2 +- .../com/iluwatar/semaphore/Semaphore.java | 12 +++++-- .../java/com/iluwatar/semaphore/AppTest.java | 34 ++++++++++++++++++ 20 files changed, 273 insertions(+), 34 deletions(-) create mode 100644 mutex/etc/mutex.png create mode 100644 mutex/index.md create mode 100644 mutex/src/test/java/com/iluwatar/mutex/AppTest.java create mode 100644 semaphore/etc/semaphore.png create mode 100644 semaphore/index.md create mode 100644 semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java diff --git a/mutex/etc/mutex.png b/mutex/etc/mutex.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7c966f87dffab72c65a77c2578c801086f7d9e GIT binary patch literal 12737 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 VGkgbka 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 RF7OUbSwT&{Qq~OqKL9K>lx6?` literal 0 HcmV?d00001 diff --git a/mutex/index.md b/mutex/index.md new file mode 100644 index 000000000000..24edad7eb76a --- /dev/null +++ b/mutex/index.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Mutex +folder: mutex +permalink: /patterns/mutex/ +categories: Lock +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Mutual Exclusion Lock +Binary Semaphore + +## Intent +Create a lock which only allows a single thread to access a resource at any one instant. + +![alt text](./etc/mutex.png "Mutex") + +## Applicability +Use a Mutex when + +* you need to prevent two threads accessing a critical section at the same time +* concurrent access to a resource could lead to a race condition + +## Credits + +* [Lock (computer science)] (http://en.wikipedia.org/wiki/Lock_(computer_science)) +* [Semaphores] (http://tutorials.jenkov.com/java-concurrency/semaphores.html) diff --git a/mutex/pom.xml b/mutex/pom.xml index 5b7058083d7c..07c2d793cda1 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -2,7 +2,7 @@ + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + data-mapper + + + + junit + junit + test + + + log4j + log4j + + + + + + + + + + src/main/resources + + + src/main/resources + + log4j.xml + + .. + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + log4j.xml + + + + true + + + + + + + + diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java new file mode 100644 index 000000000000..6345b5a4cd1c --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -0,0 +1,105 @@ +/** + * The MIT License Copyright (c) 2014 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.datamapper; + +import java.util.Optional; +import java.util.UUID; + +import org.apache.log4j.Logger; + +/** + * + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ * The below example demonstrates basic CRUD operations: select, add, update, and delete. + * + */ +public final class App { + + private static Logger log = Logger.getLogger(App.class); + + + private static final String DB_TYPE_ORACLE = "Oracle"; + private static final String DB_TYPE_MYSQL = "MySQL"; + + + /** + * Program entry point. + * + * @param args command line args. + */ + public static final void main(final String... args) { + + if (log.isInfoEnabled() & args.length > 0) { + log.debug("App.main(), db type: " + args[0]); + } + + StudentDataMapper mapper = null; + + /* Check the desired db type from runtime arguments */ + if (args.length == 0) { + + /* Create default data mapper for mysql */ + mapper = new StudentMySQLDataMapper(); + + } else if (args.length > 0 && DB_TYPE_ORACLE.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for mysql */ + mapper = new StudentMySQLDataMapper(); + + } else if (args.length > 0 && DB_TYPE_MYSQL.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for oracle */ + mapper = new StudentMySQLDataMapper(); + } else { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Following data source(" + args[0] + ") is not supported"); + } + + /* Create new student */ + Student student = new Student(UUID.randomUUID(), 1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Find this student */ + final Optional studentToBeFound = mapper.find(student.getGuId()); + + if (log.isDebugEnabled()) { + log.debug("App.main(), db find returned : " + studentToBeFound); + } + + /* Update existing student object */ + student = new Student(student.getGuId(), 1, "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Delete student in db */ + mapper.delete(student); + } + + private App() {} +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java new file mode 100644 index 000000000000..7cc66d5af5b2 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java @@ -0,0 +1,79 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +/** + * + * @author amit.dixit + * + */ +public final class DataMapperException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new runtime exception with {@code null} as its detail message. The cause is not + * initialized, and may subsequently be initialized by a call to {@link #initCause}. + */ + public DataMapperException() { + super(); + } + + + /** + * Constructs a new runtime exception with the specified detail message. The cause is not + * initialized, and may subsequently be initialized by a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public DataMapperException(final String message) { + super(message); + } + + /** + * Constructs a new runtime exception with the specified detail message and cause. + *

+ * Note that the detail message associated with {@code cause} is not automatically + * incorporated in this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval by the + * {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). + * (A null value is permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public DataMapperException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new runtime exception with the specified cause and a detail message of + * (cause==null ? null : cause.toString()) (which typically contains the class and detail + * message of cause). This constructor is useful for runtime exceptions that are little + * more than wrappers for other throwables. + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). + * (A null value is permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public DataMapperException(final Throwable cause) { + super(cause); + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java new file mode 100644 index 000000000000..920d4d9ee83b --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java @@ -0,0 +1,83 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.io.Serializable; +import java.util.UUID; + +public final class Student implements Serializable { + + private static final long serialVersionUID = 1L; + + private UUID guid; + private int studentID; + private String name; + private char grade; + + public Student() { + this.guid = UUID.randomUUID(); + } + + public Student(final UUID guid, final int studentID, final String name, final char grade) { + super(); + + this.guid = guid; + this.studentID = studentID; + this.name = name; + this.grade = grade; + } + + + public Student(final UUID guid) { + this.guid = guid; + } + + public final int getStudentId() { + return studentID; + } + + public final void setStudentId(final int studentID) { + this.studentID = studentID; + } + + public final String getName() { + return name; + } + + public final void setName(final String name) { + this.name = name; + } + + public final char getGrade() { + return grade; + } + + public final void setGrade(final char grade) { + this.grade = grade; + } + + public final UUID getGuId() { + return guid; + } + + @Override + public final String toString() { + return "Student [guid=" + guid + ", studentID=" + studentID + ", name=" + name + ", grade=" + grade + "]"; + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java new file mode 100644 index 000000000000..2493a9478109 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java @@ -0,0 +1,33 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.Optional; +import java.util.UUID; + +public interface StudentDataMapper { + + public Optional find(final UUID uniqueID) throws DataMapperException; + + public void insert(final Student student) throws DataMapperException; + + public void update(final Student student) throws DataMapperException; + + public void delete(final Student student) throws DataMapperException; +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java new file mode 100644 index 000000000000..7520bc92371e --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java @@ -0,0 +1,160 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; +import java.util.UUID; + +public final class StudentMySQLDataMapper implements StudentDataMapper { + + @Override + public final Optional find(final UUID uniqueID) throws DataMapperException { + + try { + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "SELECT `guid`, `grade`, `studentID`, `name` FROM `students` where `guid`=?"; + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set unique id in sql statement */ + dbStatement.setString(1, uniqueID.toString()); + + /* Execute the sql query */ + final ResultSet rs = dbStatement.executeQuery(); + + while (rs.next()) { + + /* Create new student */ + final Student student = new Student(UUID.fromString(rs.getString("guid"))); + + /* Set all values from database in java object */ + student.setName(rs.getString("name")); + student.setGrade(rs.getString("grade").charAt(0)); + student.setStudentId(rs.getInt("studentID")); + + return Optional.of(student); + } + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from MySQL data source.", e); + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public final void update(final Student student) throws DataMapperException { + try { + + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "UPDATE `students` SET `grade`=?, `studentID`=?, `name`=? where `guid`=?"; + + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set java object values in sql statement */ + dbStatement.setString(1, Character.toString(student.getGrade())); + dbStatement.setInt(2, student.getStudentId()); + dbStatement.setString(3, student.getName()); + dbStatement.setString(4, student.getGuId().toString()); + + /* Execute the sql query */ + dbStatement.executeUpdate(); + + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from MySQL data source.", e); + } + } + + @Override + public final void insert(final Student student) throws DataMapperException { + try { + + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "INSERT INTO `students` (`grade`, `studentID`, `name`, `guid`) VALUES (?, ?, ?, ?)"; + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set java object values in sql statement */ + dbStatement.setString(1, Character.toString(student.getGrade())); + dbStatement.setInt(2, student.getStudentId()); + dbStatement.setString(3, student.getName()); + dbStatement.setString(4, student.getGuId().toString()); + + /* Execute the sql query */ + dbStatement.executeUpdate(); + + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from MySQL data source.", e); + } + } + + @Override + public final void delete(final Student student) throws DataMapperException { + try { + + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "DELETE FROM `students` where `guid`=?"; + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set java object values in sql statement */ + dbStatement.setString(1, student.getGuId().toString()); + + /* Execute the sql query */ + dbStatement.executeUpdate(); + + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from MySQL data source.", e); + } + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java new file mode 100644 index 000000000000..0b6b7ebbfd39 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java @@ -0,0 +1,160 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; +import java.util.UUID; + +public final class StudentOracleDataMapper implements StudentDataMapper { + + @Override + public final Optional find(final UUID uniqueID) throws DataMapperException { + + try { + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "SELECT `guid`, `grade`, `studentID`, `name` FROM `students` where `guid`=?"; + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set unique id in sql statement */ + dbStatement.setString(1, uniqueID.toString()); + + /* Execute the sql query */ + final ResultSet rs = dbStatement.executeQuery(); + + while (rs.next()) { + + /* Create new student */ + final Student student = new Student(UUID.fromString(rs.getString("guid"))); + + /* Set all values from database in java object */ + student.setName(rs.getString("name")); + student.setGrade(rs.getString("grade").charAt(0)); + student.setStudentId(rs.getInt("studentID")); + + return Optional.of(student); + } + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from Oracle data source.", e); + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public final void update(final Student student) throws DataMapperException { + try { + + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "UPDATE `students` SET `grade`=?, `studentID`=?, `name`=? where `guid`=?"; + + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set java object values in sql statement */ + dbStatement.setString(1, Character.toString(student.getGrade())); + dbStatement.setInt(2, student.getStudentId()); + dbStatement.setString(3, student.getName()); + dbStatement.setString(4, student.getGuId().toString()); + + /* Execute the sql query */ + dbStatement.executeUpdate(); + + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from Oracle data source.", e); + } + } + + @Override + public final void insert(final Student student) throws DataMapperException { + try { + + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "INSERT INTO `students` (`grade`, `studentID`, `name`, `guid`) VALUES (?, ?, ?, ?)"; + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set java object values in sql statement */ + dbStatement.setString(1, Character.toString(student.getGrade())); + dbStatement.setInt(2, student.getStudentId()); + dbStatement.setString(3, student.getName()); + dbStatement.setString(4, student.getGuId().toString()); + + /* Execute the sql query */ + dbStatement.executeUpdate(); + + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from Oracle data source.", e); + } + } + + @Override + public final void delete(final Student student) throws DataMapperException { + try { + + /* OracleDriver class cant be initilized directly */ + Class.forName("oracle.jdbc.driver.OracleDriver"); + + /* Create new connection */ + final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); + + /* Create new Oracle compliant sql statement */ + final String statement = "DELETE FROM `students` where `guid`=?"; + final PreparedStatement dbStatement = connection.prepareStatement(statement); + + /* Set java object values in sql statement */ + dbStatement.setString(1, student.getGuId().toString()); + + /* Execute the sql query */ + dbStatement.executeUpdate(); + + } catch (final SQLException | ClassNotFoundException e) { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Error occured reading Students from Oracle data source.", e); + } + } +} diff --git a/data-mapper/src/main/resources/log4j.xml b/data-mapper/src/main/resources/log4j.xml new file mode 100644 index 000000000000..b591c17e1665 --- /dev/null +++ b/data-mapper/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From e2af78f417c076243cf8202b61a651485aac13b9 Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Tue, 29 Mar 2016 13:42:54 +0100 Subject: [PATCH 030/207] Update According to Review Comments #397 Resubmit of updates --- mutex/{index.md => README.md} | 0 semaphore/{index.md => README.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename mutex/{index.md => README.md} (100%) rename semaphore/{index.md => README.md} (100%) diff --git a/mutex/index.md b/mutex/README.md similarity index 100% rename from mutex/index.md rename to mutex/README.md diff --git a/semaphore/index.md b/semaphore/README.md similarity index 100% rename from semaphore/index.md rename to semaphore/README.md From 0c6d3f2c3aef3b3c47f050bbc636f68fdd036ba6 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sat, 2 Apr 2016 18:19:21 -0500 Subject: [PATCH 031/207] #297 Create Spring Boot-backed Image microservice with an endpoint to retrieve an image path --- api-gateway/image-microservice/pom.xml | 69 +++++++++++++++++++ .../image/microservice/ImageApplication.java | 21 ++++++ .../image/microservice/ImageController.java | 21 ++++++ .../src/main/resources/application.properties | 1 + 4 files changed, 112 insertions(+) create mode 100644 api-gateway/image-microservice/pom.xml create mode 100644 api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java create mode 100644 api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java create mode 100644 api-gateway/image-microservice/src/main/resources/application.properties diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml new file mode 100644 index 000000000000..114435d2147a --- /dev/null +++ b/api-gateway/image-microservice/pom.xml @@ -0,0 +1,69 @@ + + + + ../../pom.xml + java-design-patterns + com.iluwatar + 1.10.0-SNAPSHOT + + + 4.0.0 + image-microservice + jar + + + 4.2.5.RELEASE + 1.3.3.RELEASE + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + + + + + 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/ImageApplication.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java new file mode 100644 index 000000000000..8a346d7626e9 --- /dev/null +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java @@ -0,0 +1,21 @@ +package com.iluwatar.image.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * ImageApplication starts up Spring Boot, exposing endpoints for the Image microservice through + * the {@link ImageController}. + */ +@SpringBootApplication +public class ImageApplication { + + /** + * Microservice entry point + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(ImageApplication.class, args); + } +} 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 new file mode 100644 index 000000000000..b98683600c32 --- /dev/null +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000000..40e46c1ce547 --- /dev/null +++ b/api-gateway/image-microservice/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=50005 \ No newline at end of file From 01737bc64374ab367d630197b45e8ddf32b53311 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sat, 2 Apr 2016 18:19:42 -0500 Subject: [PATCH 032/207] #297 Create Spring Boot-backed Price microservice with an endpoint to retrieve a price --- api-gateway/price-microservice/pom.xml | 61 +++++++++++++++++++ .../price/microservice/PriceApplication.java | 21 +++++++ .../price/microservice/PriceController.java | 21 +++++++ .../src/main/resources/application.properties | 1 + 4 files changed, 104 insertions(+) create mode 100644 api-gateway/price-microservice/pom.xml create mode 100644 api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java create mode 100644 api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java create mode 100644 api-gateway/price-microservice/src/main/resources/application.properties diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml new file mode 100644 index 000000000000..652002bb2796 --- /dev/null +++ b/api-gateway/price-microservice/pom.xml @@ -0,0 +1,61 @@ + + + + ../../pom.xml + java-design-patterns + com.iluwatar + 1.10.0-SNAPSHOT + + + 4.0.0 + price-microservice + jar + + + 4.2.5.RELEASE + 1.3.3.RELEASE + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + + + + + 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/PriceApplication.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java new file mode 100644 index 000000000000..de2fb95b0c18 --- /dev/null +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java @@ -0,0 +1,21 @@ +package com.iluwatar.price.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * PriceApplication starts up Spring Boot, exposing endpoints for the Price microservice through + * the {@link PriceController}. + */ +@SpringBootApplication +public class PriceApplication { + + /** + * Microservice entry point + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(PriceApplication.class, args); + } +} 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 new file mode 100644 index 000000000000..1af8832eea1b --- /dev/null +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000000..49bd7ebf9ea0 --- /dev/null +++ b/api-gateway/price-microservice/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=50006 \ No newline at end of file From 665256ecc0abc9fad3cea85a1afd1efd75f37f2d Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sat, 2 Apr 2016 18:25:13 -0500 Subject: [PATCH 033/207] #297 Create Spring Boot-backed API Gateway that aggregates calls to the Image and Price microservices --- api-gateway/pom.xml | 55 +++++++++++++++++++ .../com/iluwatar/api/gateway/ApiGateway.java | 21 +++++-- .../java/com/iluwatar/api/gateway/App.java | 26 ++++----- .../iluwatar/api/gateway/DesktopProduct.java | 8 ++- .../com/iluwatar/api/gateway/ImageClient.java | 8 +++ .../iluwatar/api/gateway/ImageClientImpl.java | 34 ++++++++++++ .../iluwatar/api/gateway/ImageService.java | 15 ----- .../iluwatar/api/gateway/MobileProduct.java | 4 +- .../com/iluwatar/api/gateway/PriceClient.java | 8 +++ .../iluwatar/api/gateway/PriceClientImpl.java | 34 ++++++++++++ .../iluwatar/api/gateway/PriceService.java | 15 ----- .../src/main/resources/application.properties | 1 + .../iluwatar/api/gateway/ApiGatewayTest.java | 35 ++++++++++-- .../com/iluwatar/api/gateway/AppTest.java | 17 ------ 14 files changed, 208 insertions(+), 73 deletions(-) create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java delete mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/ImageService.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java create mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java delete mode 100644 api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java create mode 100644 api-gateway/src/main/resources/application.properties delete mode 100644 api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index bb7f5701da0d..36eef6668feb 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -9,11 +9,66 @@ 4.0.0 api-gateway + jar + + + 4.2.5.RELEASE + 1.3.3.RELEASE + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + junit junit test + + org.mockito + mockito-core + test + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + \ No newline at end of file diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java index 0ecbf7405e27..b475dfdb28f0 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java @@ -1,21 +1,31 @@ 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 { - private ImageService imageService = new ImageService(); - private PriceService priceService = new PriceService(); + @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(imageService.getImagePath()); - desktopProduct.setPrice(priceService.getPrice()); + desktopProduct.setImagePath(imageClient.getImagePath()); + desktopProduct.setPrice(priceClient.getPrice()); return desktopProduct; } @@ -23,9 +33,10 @@ public DesktopProduct getProductDesktop() { * 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(priceService.getPrice()); + mobileProduct.setPrice(priceClient.getPrice()); return mobileProduct; } } diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java index 7bad0269bb0b..29829c8d51fe 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java @@ -1,5 +1,8 @@ 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, @@ -18,13 +21,15 @@ * *

* This implementation shows what the API Gateway pattern could look like for an e-commerce site. - * In this case, the (@link ImageService) and (@link PriceService) represent our microservices. - * 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). + * 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 { /** @@ -34,13 +39,6 @@ public class App { * command line args */ public static void main(String[] args) { - ApiGateway apiGateway = new ApiGateway(); - - DesktopProduct desktopProduct = apiGateway.getProductDesktop(); - System.out.println(String.format("Desktop Product \nPrice: %s\nImage path: %s", - desktopProduct.getPrice(), desktopProduct.getImagePath())); - - MobileProduct mobileProduct = apiGateway.getProductMobile(); - System.out.println(String.format("Mobile Product \nPrice: %s", mobileProduct.getPrice())); + SpringApplication.run(App.class, args); } } diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java index 8105b01b1d2b..ba43f0997b83 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java @@ -4,8 +4,14 @@ * 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() { diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java new file mode 100644 index 000000000000..fd1ae635b3d6 --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java @@ -0,0 +1,8 @@ +package com.iluwatar.api.gateway; + +/** + * An interface used to communicate with the Image microservice + */ +public interface ImageClient { + String getImagePath(); +} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java new file mode 100644 index 000000000000..62d426e8dd1a --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java @@ -0,0 +1,34 @@ +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/src/main/java/com/iluwatar/api/gateway/ImageService.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageService.java deleted file mode 100644 index 3904f545dc92..000000000000 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageService.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iluwatar.api.gateway; - -/** - * Represents a microservice used to retrieve image data. - */ -public class ImageService { - - /** - * Gets the image path - * @return the image path - */ - public String getImagePath() { - return "/product-image.png"; - } -} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java index 772e540df64f..ed986f774770 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java @@ -4,7 +4,9 @@ * Encapsulates all of the information that mobile client needs to display a product. */ public class MobileProduct { - + /** + * The price of the product + */ private String price; public String getPrice() { diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java new file mode 100644 index 000000000000..2df566bb26f3 --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java @@ -0,0 +1,8 @@ +package com.iluwatar.api.gateway; + +/** + * An interface used to communicate with the Price microservice + */ +public interface PriceClient { + String getPrice(); +} diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java new file mode 100644 index 000000000000..67d984c315a8 --- /dev/null +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java @@ -0,0 +1,34 @@ +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/src/main/java/com/iluwatar/api/gateway/PriceService.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java deleted file mode 100644 index f64f1cb11ed3..000000000000 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceService.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iluwatar.api.gateway; - -/** - * Represents a microservice used to retrieve price data. - */ -public class PriceService { - - /** - * Gets the price - * @return the price - */ - public String getPrice() { - return "20"; - } -} diff --git a/api-gateway/src/main/resources/application.properties b/api-gateway/src/main/resources/application.properties new file mode 100644 index 000000000000..81efb206b93a --- /dev/null +++ b/api-gateway/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=50004 \ No newline at end of file diff --git a/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java index 00abdacb52a7..f434f88570f3 100644 --- a/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java +++ b/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java @@ -1,21 +1,44 @@ package com.iluwatar.api.gateway; +import org.junit.Before; import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; 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() { - ApiGateway apiGateway = new ApiGateway(); + String imagePath = "/product-image.png"; + String price = "20"; + when(imageClient.getImagePath()).thenReturn(imagePath); + when(priceClient.getPrice()).thenReturn(price); + DesktopProduct desktopProduct = apiGateway.getProductDesktop(); - assertEquals("20", desktopProduct.getPrice()); - assertEquals("/product-image.png", desktopProduct.getImagePath()); + assertEquals(price, desktopProduct.getPrice()); + assertEquals(imagePath, desktopProduct.getImagePath()); } /** @@ -23,9 +46,11 @@ public void testGetProductDesktop() { */ @Test public void testGetProductMobile() { - ApiGateway apiGateway = new ApiGateway(); + String price = "20"; + when(priceClient.getPrice()).thenReturn(price); + MobileProduct mobileProduct = apiGateway.getProductMobile(); - assertEquals("20", mobileProduct.getPrice()); + assertEquals(price, mobileProduct.getPrice()); } } diff --git a/api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java b/api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java deleted file mode 100644 index 706f34b71d9a..000000000000 --- a/api-gateway/src/test/java/com/iluwatar/api/gateway/AppTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.iluwatar.api.gateway; - -import org.junit.Test; - -/** - * - * Application test - * - */ -public class AppTest { - - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - } -} From e80583fad758433ed62ee37c5d1818d4eb43b6a8 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sat, 2 Apr 2016 18:31:58 -0500 Subject: [PATCH 034/207] #297 Create unit test for ImageController --- api-gateway/image-microservice/pom.xml | 13 +++++-------- .../image/microservice/ImageControllerTest.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index 114435d2147a..dadedcdae4f0 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -40,6 +40,11 @@ spring-boot-starter-web ${spring-boot.version} + + junit + junit + test + @@ -58,12 +63,4 @@ - - - - - - - - \ 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 new file mode 100644 index 000000000000..bac3e2ae0efc --- /dev/null +++ b/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java @@ -0,0 +1,15 @@ +package com.iluwatar.image.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class ImageControllerTest { + @Test + public void testGetImagePath() { + ImageController imageController = new ImageController(); + + String imagePath = imageController.getImagePath(); + + Assert.assertEquals("/product-image.png", imagePath); + } +} From 4ebb64c092dab6853a17bc5a1f0c786557ff2d0e Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sat, 2 Apr 2016 18:33:50 -0500 Subject: [PATCH 035/207] #297 Create unit test for PriceController --- api-gateway/price-microservice/pom.xml | 5 +++++ .../price/microservice/PriceControllerTest.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index 652002bb2796..ad5cb9ee6d97 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -40,6 +40,11 @@ spring-boot-starter-web ${spring-boot.version} + + junit + junit + test + 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 new file mode 100644 index 000000000000..230536fea577 --- /dev/null +++ b/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java @@ -0,0 +1,15 @@ +package com.iluwatar.price.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class PriceControllerTest { + @Test + public void testgetPrice() { + PriceController priceController = new PriceController(); + + String price = priceController.getPrice(); + + Assert.assertEquals("20", price); + } +} From deb15e2733bac2e68adac5a271a13d6c453f1360 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Mon, 4 Apr 2016 12:24:15 +0530 Subject: [PATCH 036/207] JDBC removed... --- .../java/com/iluwatar/datamapper/App.java | 209 +++++++------- .../java/com/iluwatar/datamapper/Student.java | 186 +++++++------ .../datamapper/StudentDataMapper.java | 65 +++-- .../datamapper/StudentMySQLDataMapper.java | 257 +++++++----------- .../datamapper/StudentOracleDataMapper.java | 257 +++++++----------- 5 files changed, 433 insertions(+), 541 deletions(-) diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java index 6345b5a4cd1c..8b5853609345 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -1,105 +1,104 @@ -/** - * The MIT License Copyright (c) 2014 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.datamapper; - -import java.util.Optional; -import java.util.UUID; - -import org.apache.log4j.Logger; - -/** - * - * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the - * database. Its responsibility is to transfer data between the two and also to isolate them from - * each other. With Data Mapper the in-memory objects needn't know even that there's a database - * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The - * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , - * Data Mapper itself is even unknown to the domain layer. - *

- * The below example demonstrates basic CRUD operations: select, add, update, and delete. - * - */ -public final class App { - - private static Logger log = Logger.getLogger(App.class); - - - private static final String DB_TYPE_ORACLE = "Oracle"; - private static final String DB_TYPE_MYSQL = "MySQL"; - - - /** - * Program entry point. - * - * @param args command line args. - */ - public static final void main(final String... args) { - - if (log.isInfoEnabled() & args.length > 0) { - log.debug("App.main(), db type: " + args[0]); - } - - StudentDataMapper mapper = null; - - /* Check the desired db type from runtime arguments */ - if (args.length == 0) { - - /* Create default data mapper for mysql */ - mapper = new StudentMySQLDataMapper(); - - } else if (args.length > 0 && DB_TYPE_ORACLE.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for mysql */ - mapper = new StudentMySQLDataMapper(); - - } else if (args.length > 0 && DB_TYPE_MYSQL.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for oracle */ - mapper = new StudentMySQLDataMapper(); - } else { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Following data source(" + args[0] + ") is not supported"); - } - - /* Create new student */ - Student student = new Student(UUID.randomUUID(), 1, "Adam", 'A'); - - /* Add student in respectibe db */ - mapper.insert(student); - - /* Find this student */ - final Optional studentToBeFound = mapper.find(student.getGuId()); - - if (log.isDebugEnabled()) { - log.debug("App.main(), db find returned : " + studentToBeFound); - } - - /* Update existing student object */ - student = new Student(student.getGuId(), 1, "AdamUpdated", 'A'); - - /* Update student in respectibe db */ - mapper.update(student); - - /* Delete student in db */ - mapper.delete(student); - } - - private App() {} -} +/** + * The MIT License Copyright (c) 2014 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.datamapper; + +import java.util.Optional; + +import org.apache.log4j.Logger; + +/** + * + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ * The below example demonstrates basic CRUD operations: Create, Read, Update, and Delete. + * + */ +public final class App { + + private static Logger log = Logger.getLogger(App.class); + + + private static final String DB_TYPE_ORACLE = "Oracle"; + private static final String DB_TYPE_MYSQL = "MySQL"; + + + /** + * Program entry point. + * + * @param args command line args. + */ + public static final void main(final String... args) { + + if (log.isInfoEnabled() & args.length > 0) { + log.debug("App.main(), db type: " + args[0]); + } + + StudentDataMapper mapper = null; + + /* Check the desired db type from runtime arguments */ + if (args.length == 0) { + + /* Create default data mapper for mysql */ + mapper = new StudentMySQLDataMapper(); + + } else if (args.length > 0 && DB_TYPE_ORACLE.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for mysql */ + mapper = new StudentMySQLDataMapper(); + + } else if (args.length > 0 && DB_TYPE_MYSQL.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for oracle */ + mapper = new StudentMySQLDataMapper(); + } else { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Following data mapping type(" + args[0] + ") is not supported"); + } + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Find this student */ + final Optional studentToBeFound = mapper.find(student.getStudentId()); + + if (log.isDebugEnabled()) { + log.debug("App.main(), db find returned : " + studentToBeFound); + } + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Delete student in db */ + mapper.delete(student); + } + + private App() {} +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java index 920d4d9ee83b..2f0c6d0a6800 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java @@ -1,83 +1,103 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.io.Serializable; -import java.util.UUID; - -public final class Student implements Serializable { - - private static final long serialVersionUID = 1L; - - private UUID guid; - private int studentID; - private String name; - private char grade; - - public Student() { - this.guid = UUID.randomUUID(); - } - - public Student(final UUID guid, final int studentID, final String name, final char grade) { - super(); - - this.guid = guid; - this.studentID = studentID; - this.name = name; - this.grade = grade; - } - - - public Student(final UUID guid) { - this.guid = guid; - } - - public final int getStudentId() { - return studentID; - } - - public final void setStudentId(final int studentID) { - this.studentID = studentID; - } - - public final String getName() { - return name; - } - - public final void setName(final String name) { - this.name = name; - } - - public final char getGrade() { - return grade; - } - - public final void setGrade(final char grade) { - this.grade = grade; - } - - public final UUID getGuId() { - return guid; - } - - @Override - public final String toString() { - return "Student [guid=" + guid + ", studentID=" + studentID + ", name=" + name + ", grade=" + grade + "]"; - } -} +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + + +import java.io.Serializable; + +public final class Student implements Serializable { + + private static final long serialVersionUID = 1L; + + private int studentId; + private String name; + private char grade; + + public Student() { + + } + + public Student(final int studentId, final String name, final char grade) { + super(); + + this.studentId = studentId; + this.name = name; + this.grade = grade; + } + + public final int getStudentId() { + return studentId; + } + + public final void setStudentId(final int studentId) { + this.studentId = studentId; + } + + public final String getName() { + return name; + } + + public final void setName(final String name) { + this.name = name; + } + + public final char getGrade() { + return grade; + } + + public final void setGrade(final char grade) { + this.grade = grade; + } + + @Override + public boolean equals(final Object inputObject) { + + boolean isEqual = false; + + /* Check if both objects are same */ + if (this == inputObject) { + + isEqual = true; + } + /* Check if objects belong to same class */ + else if (inputObject != null && getClass() == inputObject.getClass()) { + + final Student student = (Student) inputObject; + + /* If student id matched */ + if (this.getStudentId() == student.getStudentId()) { + + isEqual = true; + } + } + return isEqual; + } + + @Override + public int hashCode() { + + /* Student id is assumed to be unique */ + return this.getStudentId(); + } + + @Override + public final String toString() { + return "Student [studentId=" + studentId + ", name=" + name + ", grade=" + grade + "]"; + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java index 2493a9478109..1fa33e067684 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java @@ -1,33 +1,32 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.util.Optional; -import java.util.UUID; - -public interface StudentDataMapper { - - public Optional find(final UUID uniqueID) throws DataMapperException; - - public void insert(final Student student) throws DataMapperException; - - public void update(final Student student) throws DataMapperException; - - public void delete(final Student student) throws DataMapperException; -} +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.Optional; + +public interface StudentDataMapper { + + public Optional find(final int studentId); + + public void insert(final Student student) throws DataMapperException; + + public void update(final Student student) throws DataMapperException; + + public void delete(final Student student) throws DataMapperException; +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java index 7520bc92371e..cbb97bfa9107 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java @@ -1,160 +1,97 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Optional; -import java.util.UUID; - -public final class StudentMySQLDataMapper implements StudentDataMapper { - - @Override - public final Optional find(final UUID uniqueID) throws DataMapperException { - - try { - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "SELECT `guid`, `grade`, `studentID`, `name` FROM `students` where `guid`=?"; - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set unique id in sql statement */ - dbStatement.setString(1, uniqueID.toString()); - - /* Execute the sql query */ - final ResultSet rs = dbStatement.executeQuery(); - - while (rs.next()) { - - /* Create new student */ - final Student student = new Student(UUID.fromString(rs.getString("guid"))); - - /* Set all values from database in java object */ - student.setName(rs.getString("name")); - student.setGrade(rs.getString("grade").charAt(0)); - student.setStudentId(rs.getInt("studentID")); - - return Optional.of(student); - } - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from MySQL data source.", e); - } - - /* Return empty value */ - return Optional.empty(); - } - - @Override - public final void update(final Student student) throws DataMapperException { - try { - - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "UPDATE `students` SET `grade`=?, `studentID`=?, `name`=? where `guid`=?"; - - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set java object values in sql statement */ - dbStatement.setString(1, Character.toString(student.getGrade())); - dbStatement.setInt(2, student.getStudentId()); - dbStatement.setString(3, student.getName()); - dbStatement.setString(4, student.getGuId().toString()); - - /* Execute the sql query */ - dbStatement.executeUpdate(); - - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from MySQL data source.", e); - } - } - - @Override - public final void insert(final Student student) throws DataMapperException { - try { - - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "INSERT INTO `students` (`grade`, `studentID`, `name`, `guid`) VALUES (?, ?, ?, ?)"; - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set java object values in sql statement */ - dbStatement.setString(1, Character.toString(student.getGrade())); - dbStatement.setInt(2, student.getStudentId()); - dbStatement.setString(3, student.getName()); - dbStatement.setString(4, student.getGuId().toString()); - - /* Execute the sql query */ - dbStatement.executeUpdate(); - - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from MySQL data source.", e); - } - } - - @Override - public final void delete(final Student student) throws DataMapperException { - try { - - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/MySQL", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "DELETE FROM `students` where `guid`=?"; - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set java object values in sql statement */ - dbStatement.setString(1, student.getGuId().toString()); - - /* Execute the sql query */ - dbStatement.executeUpdate(); - - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from MySQL data source.", e); - } - } -} +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.List; +import java.util.Optional; + +public final class StudentMySQLDataMapper implements StudentDataMapper { + + /* Note: Normally this would be in the form of an actual database */ + private List students; + + @Override + public final Optional find(final int studentId) { + + /* Compare with existing students */ + for (final Student student : this.students) { + + /* Check if student is found */ + if (student.getStudentId() == studentId) { + + return Optional.of(student); + } + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public final void update(final Student studentToBeUpdated) throws DataMapperException { + + + /* Check with existing students */ + if (this.students.contains(studentToBeUpdated)) { + + /* Get the index of student in list */ + final int index = this.students.indexOf(studentToBeUpdated); + + /* Update the student in list */ + this.students.set(index, studentToBeUpdated); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); + } + } + + @Override + public final void insert(final Student studentToBeInserted) throws DataMapperException { + + /* Check with existing students */ + if (!this.students.contains(studentToBeInserted)) { + + /* Add student in list */ + this.students.add(studentToBeInserted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); + } + } + + @Override + public final void delete(final Student studentToBeDeleted) throws DataMapperException { + + /* Check with existing students */ + if (this.students.contains(studentToBeDeleted)) { + + /* Delete the student from list */ + this.students.remove(studentToBeDeleted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); + } + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java index 0b6b7ebbfd39..6abf1c55a0ec 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java @@ -1,160 +1,97 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Optional; -import java.util.UUID; - -public final class StudentOracleDataMapper implements StudentDataMapper { - - @Override - public final Optional find(final UUID uniqueID) throws DataMapperException { - - try { - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "SELECT `guid`, `grade`, `studentID`, `name` FROM `students` where `guid`=?"; - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set unique id in sql statement */ - dbStatement.setString(1, uniqueID.toString()); - - /* Execute the sql query */ - final ResultSet rs = dbStatement.executeQuery(); - - while (rs.next()) { - - /* Create new student */ - final Student student = new Student(UUID.fromString(rs.getString("guid"))); - - /* Set all values from database in java object */ - student.setName(rs.getString("name")); - student.setGrade(rs.getString("grade").charAt(0)); - student.setStudentId(rs.getInt("studentID")); - - return Optional.of(student); - } - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from Oracle data source.", e); - } - - /* Return empty value */ - return Optional.empty(); - } - - @Override - public final void update(final Student student) throws DataMapperException { - try { - - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "UPDATE `students` SET `grade`=?, `studentID`=?, `name`=? where `guid`=?"; - - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set java object values in sql statement */ - dbStatement.setString(1, Character.toString(student.getGrade())); - dbStatement.setInt(2, student.getStudentId()); - dbStatement.setString(3, student.getName()); - dbStatement.setString(4, student.getGuId().toString()); - - /* Execute the sql query */ - dbStatement.executeUpdate(); - - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from Oracle data source.", e); - } - } - - @Override - public final void insert(final Student student) throws DataMapperException { - try { - - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "INSERT INTO `students` (`grade`, `studentID`, `name`, `guid`) VALUES (?, ?, ?, ?)"; - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set java object values in sql statement */ - dbStatement.setString(1, Character.toString(student.getGrade())); - dbStatement.setInt(2, student.getStudentId()); - dbStatement.setString(3, student.getName()); - dbStatement.setString(4, student.getGuId().toString()); - - /* Execute the sql query */ - dbStatement.executeUpdate(); - - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from Oracle data source.", e); - } - } - - @Override - public final void delete(final Student student) throws DataMapperException { - try { - - /* OracleDriver class cant be initilized directly */ - Class.forName("oracle.jdbc.driver.OracleDriver"); - - /* Create new connection */ - final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:Oracle", "username", "password"); - - /* Create new Oracle compliant sql statement */ - final String statement = "DELETE FROM `students` where `guid`=?"; - final PreparedStatement dbStatement = connection.prepareStatement(statement); - - /* Set java object values in sql statement */ - dbStatement.setString(1, student.getGuId().toString()); - - /* Execute the sql query */ - dbStatement.executeUpdate(); - - } catch (final SQLException | ClassNotFoundException e) { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Error occured reading Students from Oracle data source.", e); - } - } -} +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.List; +import java.util.Optional; + +public final class StudentOracleDataMapper implements StudentDataMapper { + + /* Note: Normally this would be in the form of an actual database */ + private List students; + + @Override + public final Optional find(final int studentId) { + + /* Compare with existing students */ + for (final Student student : this.students) { + + /* Check if student is found */ + if (student.getStudentId() == studentId) { + + return Optional.of(student); + } + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public final void update(final Student studentToBeUpdated) throws DataMapperException { + + + /* Check with existing students */ + if (this.students.contains(studentToBeUpdated)) { + + /* Get the index of student in list */ + final int index = this.students.indexOf(studentToBeUpdated); + + /* Update the student in list */ + this.students.set(index, studentToBeUpdated); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); + } + } + + @Override + public final void insert(final Student studentToBeInserted) throws DataMapperException { + + /* Check with existing students */ + if (!this.students.contains(studentToBeInserted)) { + + /* Add student in list */ + this.students.add(studentToBeInserted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); + } + } + + @Override + public final void delete(final Student studentToBeDeleted) throws DataMapperException { + + /* Check with existing students */ + if (this.students.contains(studentToBeDeleted)) { + + /* Delete the student from list */ + this.students.remove(studentToBeDeleted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); + } + } +} From eb72493f1363b0ab4aa381f636aa9ebab2333158 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Mon, 4 Apr 2016 12:33:43 +0530 Subject: [PATCH 037/207] JDBC removed... --- .../java/com/iluwatar/datamapper/App.java | 14 ++- .../datamapper/StudentMySQLDataMapper.java | 23 +++-- .../datamapper/StudentOracleDataMapper.java | 23 +++-- .../java/com/iluwatar/datamapper/AppTest.java | 99 +++++++++++++++++++ 4 files changed, 140 insertions(+), 19 deletions(-) create mode 100644 data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java index 8b5853609345..d8a968a171bf 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -83,11 +83,15 @@ public static final void main(final String... args) { /* Add student in respectibe db */ mapper.insert(student); + if (log.isDebugEnabled()) { + log.debug("App.main(), student : " + student + ", is inserted"); + } + /* Find this student */ final Optional studentToBeFound = mapper.find(student.getStudentId()); if (log.isDebugEnabled()) { - log.debug("App.main(), db find returned : " + studentToBeFound); + log.debug("App.main(), student : " + studentToBeFound + ", is searched"); } /* Update existing student object */ @@ -96,7 +100,15 @@ public static final void main(final String... args) { /* Update student in respectibe db */ mapper.update(student); + if (log.isDebugEnabled()) { + log.debug("App.main(), student : " + student + ", is updated"); + } + /* Delete student in db */ + + if (log.isDebugEnabled()) { + log.debug("App.main(), student : " + student + ", is deleted"); + } mapper.delete(student); } diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java index cbb97bfa9107..ec7507eedc85 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java @@ -18,19 +18,20 @@ */ package com.iluwatar.datamapper; +import java.util.ArrayList; import java.util.List; import java.util.Optional; public final class StudentMySQLDataMapper implements StudentDataMapper { /* Note: Normally this would be in the form of an actual database */ - private List students; + private List students = new ArrayList<>(); @Override public final Optional find(final int studentId) { /* Compare with existing students */ - for (final Student student : this.students) { + for (final Student student : this.getStudents()) { /* Check if student is found */ if (student.getStudentId() == studentId) { @@ -48,13 +49,13 @@ public final void update(final Student studentToBeUpdated) throws DataMapperExce /* Check with existing students */ - if (this.students.contains(studentToBeUpdated)) { + if (this.getStudents().contains(studentToBeUpdated)) { /* Get the index of student in list */ - final int index = this.students.indexOf(studentToBeUpdated); + final int index = this.getStudents().indexOf(studentToBeUpdated); /* Update the student in list */ - this.students.set(index, studentToBeUpdated); + this.getStudents().set(index, studentToBeUpdated); } else { @@ -67,10 +68,10 @@ public final void update(final Student studentToBeUpdated) throws DataMapperExce public final void insert(final Student studentToBeInserted) throws DataMapperException { /* Check with existing students */ - if (!this.students.contains(studentToBeInserted)) { + if (!this.getStudents().contains(studentToBeInserted)) { /* Add student in list */ - this.students.add(studentToBeInserted); + this.getStudents().add(studentToBeInserted); } else { @@ -83,10 +84,10 @@ public final void insert(final Student studentToBeInserted) throws DataMapperExc public final void delete(final Student studentToBeDeleted) throws DataMapperException { /* Check with existing students */ - if (this.students.contains(studentToBeDeleted)) { + if (this.getStudents().contains(studentToBeDeleted)) { /* Delete the student from list */ - this.students.remove(studentToBeDeleted); + this.getStudents().remove(studentToBeDeleted); } else { @@ -94,4 +95,8 @@ public final void delete(final Student studentToBeDeleted) throws DataMapperExce throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); } } + + public List getStudents() { + return this.students; + } } diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java index 6abf1c55a0ec..ca66f11c9201 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java @@ -18,19 +18,20 @@ */ package com.iluwatar.datamapper; +import java.util.ArrayList; import java.util.List; import java.util.Optional; public final class StudentOracleDataMapper implements StudentDataMapper { /* Note: Normally this would be in the form of an actual database */ - private List students; + private List students = new ArrayList<>(); @Override public final Optional find(final int studentId) { /* Compare with existing students */ - for (final Student student : this.students) { + for (final Student student : this.getStudents()) { /* Check if student is found */ if (student.getStudentId() == studentId) { @@ -48,13 +49,13 @@ public final void update(final Student studentToBeUpdated) throws DataMapperExce /* Check with existing students */ - if (this.students.contains(studentToBeUpdated)) { + if (this.getStudents().contains(studentToBeUpdated)) { /* Get the index of student in list */ - final int index = this.students.indexOf(studentToBeUpdated); + final int index = this.getStudents().indexOf(studentToBeUpdated); /* Update the student in list */ - this.students.set(index, studentToBeUpdated); + this.getStudents().set(index, studentToBeUpdated); } else { @@ -67,10 +68,10 @@ public final void update(final Student studentToBeUpdated) throws DataMapperExce public final void insert(final Student studentToBeInserted) throws DataMapperException { /* Check with existing students */ - if (!this.students.contains(studentToBeInserted)) { + if (!this.getStudents().contains(studentToBeInserted)) { /* Add student in list */ - this.students.add(studentToBeInserted); + this.getStudents().add(studentToBeInserted); } else { @@ -83,10 +84,10 @@ public final void insert(final Student studentToBeInserted) throws DataMapperExc public final void delete(final Student studentToBeDeleted) throws DataMapperException { /* Check with existing students */ - if (this.students.contains(studentToBeDeleted)) { + if (this.getStudents().contains(studentToBeDeleted)) { /* Delete the student from list */ - this.students.remove(studentToBeDeleted); + this.getStudents().remove(studentToBeDeleted); } else { @@ -94,4 +95,8 @@ public final void delete(final Student studentToBeDeleted) throws DataMapperExce throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); } } + + public List getStudents() { + return this.students; + } } diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java new file mode 100644 index 000000000000..6e415bccb9dc --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java @@ -0,0 +1,99 @@ +/** + * The MIT License Copyright (c) 2014 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.datamapper; + +import java.util.Optional; +import java.util.UUID; + +import org.apache.log4j.Logger; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ * The below example demonstrates basic CRUD operations: select, add, update, and delete. + * + */ +public final class App { + + private static Logger log = Logger.getLogger(App.class); + + + private static final String DB_TYPE_ORACLE = "Oracle"; + private static final String DB_TYPE_MYSQL = "MySQL"; + + + /** + * Program entry point. + * + * @param args command line args. + */ + public static final void main(final String... args) { + + if (log.isInfoEnabled() & args.length > 0) { + log.debug("App.main(), db type: " + args[0]); + } + + StudentDataMapper mapper = null; + + /* Check the desired db type from runtime arguments */ + if (DB_TYPE_ORACLE.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for mysql */ + mapper = new StudentMySQLDataMapper(); + + } else if (DB_TYPE_MYSQL.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for oracle */ + mapper = new StudentMySQLDataMapper(); + } else { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Following data source(" + args[0] + ") is not supported"); + } + + /* Create new student */ + Student student = new Student(UUID.randomUUID(), 1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Find this student */ + final Optional studentToBeFound = mapper.find(student.getGuId()); + + if (log.isDebugEnabled()) { + log.debug("App.main(), db find returned : " + studentToBeFound); + } + + /* Update existing student object */ + student = new Student(student.getGuId(), 1, "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Delete student in db */ + mapper.delete(student); + } + + private App() {} +} From 59b6b817f4a6e02cd56d781283f148e7e65c04aa Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Mon, 4 Apr 2016 15:31:43 +0530 Subject: [PATCH 038/207] Test/Doc added Test/Doc added --- data-mapper/etc/data-mapper.png | Bin 0 -> 54062 bytes data-mapper/etc/data-mapper.ucls | 94 ++++++++ data-mapper/index.md | 2 +- .../java/com/iluwatar/datamapper/App.java | 34 +-- .../java/com/iluwatar/datamapper/Student.java | 66 ++++-- .../datamapper/StudentDataMapper.java | 8 +- ...apper.java => StudentFirstDataMapper.java} | 204 +++++++++--------- ...pper.java => StudentSecondDataMapper.java} | 203 +++++++++-------- .../java/com/iluwatar/datamapper/AppTest.java | 84 +------- .../iluwatar/datamapper/DataMapperTest.java | 108 ++++++++++ 10 files changed, 475 insertions(+), 328 deletions(-) create mode 100644 data-mapper/etc/data-mapper.png create mode 100644 data-mapper/etc/data-mapper.ucls rename data-mapper/src/main/java/com/iluwatar/datamapper/{StudentMySQLDataMapper.java => StudentFirstDataMapper.java} (85%) rename data-mapper/src/main/java/com/iluwatar/datamapper/{StudentOracleDataMapper.java => StudentSecondDataMapper.java} (85%) create mode 100644 data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java diff --git a/data-mapper/etc/data-mapper.png b/data-mapper/etc/data-mapper.png new file mode 100644 index 0000000000000000000000000000000000000000..8fc90f591deb0c08aa2479c37a268ef0b5d5de2c GIT binary patch literal 54062 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 literal 0 HcmV?d00001 diff --git a/data-mapper/etc/data-mapper.ucls b/data-mapper/etc/data-mapper.ucls new file mode 100644 index 000000000000..ff10fc32fe57 --- /dev/null +++ b/data-mapper/etc/data-mapper.ucls @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data-mapper/index.md b/data-mapper/index.md index 4e640d5d6a28..684595c53c79 100644 --- a/data-mapper/index.md +++ b/data-mapper/index.md @@ -13,7 +13,7 @@ tags: Object provides an abstract interface to some type of database or other persistence mechanism. -![alt text](./etc/dm.png "Data Mapper") +![alt text](./etc/data-mapper.png "Data Mapper") ## Applicability Use the Data Mapper in any of the following situations diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java index d8a968a171bf..63e3af41dead 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -23,7 +23,6 @@ import org.apache.log4j.Logger; /** - * * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the * database. Its responsibility is to transfer data between the two and also to isolate them from * each other. With Data Mapper the in-memory objects needn't know even that there's a database @@ -39,43 +38,18 @@ public final class App { private static Logger log = Logger.getLogger(App.class); - private static final String DB_TYPE_ORACLE = "Oracle"; - private static final String DB_TYPE_MYSQL = "MySQL"; - /** * Program entry point. * * @param args command line args. */ - public static final void main(final String... args) { - - if (log.isInfoEnabled() & args.length > 0) { - log.debug("App.main(), db type: " + args[0]); - } - - StudentDataMapper mapper = null; - - /* Check the desired db type from runtime arguments */ - if (args.length == 0) { + public static void main(final String... args) { - /* Create default data mapper for mysql */ - mapper = new StudentMySQLDataMapper(); - } else if (args.length > 0 && DB_TYPE_ORACLE.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for mysql */ - mapper = new StudentMySQLDataMapper(); - - } else if (args.length > 0 && DB_TYPE_MYSQL.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for oracle */ - mapper = new StudentMySQLDataMapper(); - } else { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Following data mapping type(" + args[0] + ") is not supported"); - } + /* Create any type of mapper at implementation which is desired */ + /* final StudentDataMapper mapper = new StudentFirstDataMapper(); */ + final StudentDataMapper mapper = new StudentSecondDataMapper(); /* Create new student */ Student student = new Student(1, "Adam", 'A'); diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java index 2f0c6d0a6800..0164533c85fc 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java @@ -29,10 +29,14 @@ public final class Student implements Serializable { private String name; private char grade; - public Student() { - - } + /** + * Use this constructor to create a Student with all details + * + * @param studentId as unique student id + * @param name as student name + * @param grade as respective grade of student + */ public Student(final int studentId, final String name, final char grade) { super(); @@ -41,30 +45,57 @@ public Student(final int studentId, final String name, final char grade) { this.grade = grade; } - public final int getStudentId() { + /** + * + * @return the student id + */ + public int getStudentId() { return studentId; } - public final void setStudentId(final int studentId) { + /** + * + * @param studentId as unique student id + */ + public void setStudentId(final int studentId) { this.studentId = studentId; } - public final String getName() { + /** + * + * @return name of student + */ + public String getName() { return name; } - public final void setName(final String name) { + /** + * + * @param name as 'name' of student + */ + public void setName(final String name) { this.name = name; } - public final char getGrade() { + /** + * + * @return grade of student + */ + public char getGrade() { return grade; } - public final void setGrade(final char grade) { + /** + * + * @param grade as 'grade of student' + */ + public void setGrade(final char grade) { this.grade = grade; } + /** + * + */ @Override public boolean equals(final Object inputObject) { @@ -74,21 +105,23 @@ public boolean equals(final Object inputObject) { if (this == inputObject) { isEqual = true; - } - /* Check if objects belong to same class */ - else if (inputObject != null && getClass() == inputObject.getClass()) { + } else if (inputObject != null && getClass() == inputObject.getClass()) { - final Student student = (Student) inputObject; + final Student inputStudent = (Student) inputObject; /* If student id matched */ - if (this.getStudentId() == student.getStudentId()) { + if (this.getStudentId() == inputStudent.getStudentId()) { isEqual = true; } } + return isEqual; } + /** + * + */ @Override public int hashCode() { @@ -96,8 +129,11 @@ public int hashCode() { return this.getStudentId(); } + /** + * + */ @Override - public final String toString() { + public String toString() { return "Student [studentId=" + studentId + ", name=" + name + ", grade=" + grade + "]"; } } diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java index 1fa33e067684..40f0c5c72052 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java @@ -22,11 +22,11 @@ public interface StudentDataMapper { - public Optional find(final int studentId); + Optional find(int studentId); - public void insert(final Student student) throws DataMapperException; + void insert(Student student) throws DataMapperException; - public void update(final Student student) throws DataMapperException; + void update(Student student) throws DataMapperException; - public void delete(final Student student) throws DataMapperException; + void delete(Student student) throws DataMapperException; } diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentFirstDataMapper.java similarity index 85% rename from data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java rename to data-mapper/src/main/java/com/iluwatar/datamapper/StudentFirstDataMapper.java index ec7507eedc85..97f6d395d6de 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentMySQLDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentFirstDataMapper.java @@ -1,102 +1,102 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -public final class StudentMySQLDataMapper implements StudentDataMapper { - - /* Note: Normally this would be in the form of an actual database */ - private List students = new ArrayList<>(); - - @Override - public final Optional find(final int studentId) { - - /* Compare with existing students */ - for (final Student student : this.getStudents()) { - - /* Check if student is found */ - if (student.getStudentId() == studentId) { - - return Optional.of(student); - } - } - - /* Return empty value */ - return Optional.empty(); - } - - @Override - public final void update(final Student studentToBeUpdated) throws DataMapperException { - - - /* Check with existing students */ - if (this.getStudents().contains(studentToBeUpdated)) { - - /* Get the index of student in list */ - final int index = this.getStudents().indexOf(studentToBeUpdated); - - /* Update the student in list */ - this.getStudents().set(index, studentToBeUpdated); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); - } - } - - @Override - public final void insert(final Student studentToBeInserted) throws DataMapperException { - - /* Check with existing students */ - if (!this.getStudents().contains(studentToBeInserted)) { - - /* Add student in list */ - this.getStudents().add(studentToBeInserted); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); - } - } - - @Override - public final void delete(final Student studentToBeDeleted) throws DataMapperException { - - /* Check with existing students */ - if (this.getStudents().contains(studentToBeDeleted)) { - - /* Delete the student from list */ - this.getStudents().remove(studentToBeDeleted); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); - } - } - - public List getStudents() { - return this.students; - } -} +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class StudentFirstDataMapper implements StudentDataMapper { + + /* Note: Normally this would be in the form of an actual database */ + private List students = new ArrayList<>(); + + @Override + public Optional find(int studentId) { + + /* Compare with existing students */ + for (final Student student : this.getStudents()) { + + /* Check if student is found */ + if (student.getStudentId() == studentId) { + + return Optional.of(student); + } + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public void update(Student studentToBeUpdated) throws DataMapperException { + + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeUpdated)) { + + /* Get the index of student in list */ + final int index = this.getStudents().indexOf(studentToBeUpdated); + + /* Update the student in list */ + this.getStudents().set(index, studentToBeUpdated); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); + } + } + + @Override + public void insert(Student studentToBeInserted) throws DataMapperException { + + /* Check with existing students */ + if (!this.getStudents().contains(studentToBeInserted)) { + + /* Add student in list */ + this.getStudents().add(studentToBeInserted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); + } + } + + @Override + public void delete(Student studentToBeDeleted) throws DataMapperException { + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeDeleted)) { + + /* Delete the student from list */ + this.getStudents().remove(studentToBeDeleted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); + } + } + + public List getStudents() { + return this.students; + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java similarity index 85% rename from data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java rename to data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java index ca66f11c9201..62d39f90d4fe 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentOracleDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java @@ -1,102 +1,101 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -public final class StudentOracleDataMapper implements StudentDataMapper { - - /* Note: Normally this would be in the form of an actual database */ - private List students = new ArrayList<>(); - - @Override - public final Optional find(final int studentId) { - - /* Compare with existing students */ - for (final Student student : this.getStudents()) { - - /* Check if student is found */ - if (student.getStudentId() == studentId) { - - return Optional.of(student); - } - } - - /* Return empty value */ - return Optional.empty(); - } - - @Override - public final void update(final Student studentToBeUpdated) throws DataMapperException { - - - /* Check with existing students */ - if (this.getStudents().contains(studentToBeUpdated)) { - - /* Get the index of student in list */ - final int index = this.getStudents().indexOf(studentToBeUpdated); - - /* Update the student in list */ - this.getStudents().set(index, studentToBeUpdated); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); - } - } - - @Override - public final void insert(final Student studentToBeInserted) throws DataMapperException { - - /* Check with existing students */ - if (!this.getStudents().contains(studentToBeInserted)) { - - /* Add student in list */ - this.getStudents().add(studentToBeInserted); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); - } - } - - @Override - public final void delete(final Student studentToBeDeleted) throws DataMapperException { - - /* Check with existing students */ - if (this.getStudents().contains(studentToBeDeleted)) { - - /* Delete the student from list */ - this.getStudents().remove(studentToBeDeleted); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); - } - } - - public List getStudents() { - return this.students; - } -} +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class StudentSecondDataMapper implements StudentDataMapper { + + /* Note: Normally this would be in the form of an actual database */ + private List students = new ArrayList<>(); + + @Override + public Optional find(int studentId) { + + /* Compare with existing students */ + for (final Student student : this.getStudents()) { + + /* Check if student is found */ + if (student.getStudentId() == studentId) { + + return Optional.of(student); + } + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public void update(Student studentToBeUpdated) throws DataMapperException { + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeUpdated)) { + + /* Get the index of student in list */ + final int index = this.getStudents().indexOf(studentToBeUpdated); + + /* Update the student in list */ + this.getStudents().set(index, studentToBeUpdated); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); + } + } + + @Override + public void insert(Student studentToBeInserted) throws DataMapperException { + + /* Check with existing students */ + if (!this.getStudents().contains(studentToBeInserted)) { + + /* Add student in list */ + this.getStudents().add(studentToBeInserted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); + } + } + + @Override + public void delete(Student studentToBeDeleted) throws DataMapperException { + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeDeleted)) { + + /* Delete the student from list */ + this.getStudents().remove(studentToBeDeleted); + + } else { + + /* Throw user error */ + throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); + } + } + + public List getStudents() { + return this.students; + } +} diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java index 6e415bccb9dc..d3ad2dcb392e 100644 --- a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java @@ -1,5 +1,6 @@ /** - * The MIT License Copyright (c) 2014 Ilkka Seppälä + * The MIT License Copyright (c) 2016 Amit Dixit + * * * 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, @@ -18,82 +19,17 @@ */ package com.iluwatar.datamapper; -import java.util.Optional; -import java.util.UUID; - -import org.apache.log4j.Logger; +import com.iluwatar.datamapper.App; +import org.junit.Test; /** - * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the - * database. Its responsibility is to transfer data between the two and also to isolate them from - * each other. With Data Mapper the in-memory objects needn't know even that there's a database - * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The - * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , - * Data Mapper itself is even unknown to the domain layer. - *

- * The below example demonstrates basic CRUD operations: select, add, update, and delete. - * + * Tests that Data-Mapper example runs without errors. */ -public final class App { - - private static Logger log = Logger.getLogger(App.class); - - - private static final String DB_TYPE_ORACLE = "Oracle"; - private static final String DB_TYPE_MYSQL = "MySQL"; - - - /** - * Program entry point. - * - * @param args command line args. - */ - public static final void main(final String... args) { - - if (log.isInfoEnabled() & args.length > 0) { - log.debug("App.main(), db type: " + args[0]); - } - - StudentDataMapper mapper = null; - - /* Check the desired db type from runtime arguments */ - if (DB_TYPE_ORACLE.equalsIgnoreCase(args[0])) { +public final class AppTest { - /* Create new data mapper for mysql */ - mapper = new StudentMySQLDataMapper(); - - } else if (DB_TYPE_MYSQL.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for oracle */ - mapper = new StudentMySQLDataMapper(); - } else { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Following data source(" + args[0] + ") is not supported"); - } - - /* Create new student */ - Student student = new Student(UUID.randomUUID(), 1, "Adam", 'A'); - - /* Add student in respectibe db */ - mapper.insert(student); - - /* Find this student */ - final Optional studentToBeFound = mapper.find(student.getGuId()); - - if (log.isDebugEnabled()) { - log.debug("App.main(), db find returned : " + studentToBeFound); - } - - /* Update existing student object */ - student = new Student(student.getGuId(), 1, "AdamUpdated", 'A'); - - /* Update student in respectibe db */ - mapper.update(student); - - /* Delete student in db */ - mapper.delete(student); + @Test + public void test() { + final String[] args = {}; + App.main(args); } - - private App() {} } diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java new file mode 100644 index 000000000000..f77097df8663 --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java @@ -0,0 +1,108 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.iluwatar.datamapper.Student; +import com.iluwatar.datamapper.StudentDataMapper; +import com.iluwatar.datamapper.StudentFirstDataMapper; +import com.iluwatar.datamapper.StudentSecondDataMapper; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ */ +public class DataMapperTest { + + /** + * This test verify that first data mapper is able to perform all CRUD operations on Student + */ + @Test + public void testFirstDataMapper() { + + /* Create new data mapper of first type */ + final StudentDataMapper mapper = new StudentFirstDataMapper(); + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Check if student is added in db */ + assertEquals(student.getStudentId(), mapper.find(student.getStudentId()).get().getStudentId()); + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Check if student is updated in db */ + assertEquals(mapper.find(student.getStudentId()).get().getName(), "AdamUpdated"); + + /* Delete student in db */ + mapper.delete(student); + + /* Result should be false */ + assertEquals(false, mapper.find(student.getStudentId()).isPresent()); + } + + /** + * This test verify that second data mapper is able to perform all CRUD operations on Student + */ + @Test + public void testSecondDataMapper() { + + /* Create new data mapper of second type */ + final StudentDataMapper mapper = new StudentSecondDataMapper(); + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Check if student is added in db */ + assertEquals(student.getStudentId(), mapper.find(student.getStudentId()).get().getStudentId()); + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Check if student is updated in db */ + assertEquals(mapper.find(student.getStudentId()).get().getName(), "AdamUpdated"); + + /* Delete student in db */ + mapper.delete(student); + + /* Result should be false */ + assertEquals(false, mapper.find(student.getStudentId()).isPresent()); + } +} From 822ab8d9fd112bd71c82c211461c6d20a33b4325 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Mon, 4 Apr 2016 16:35:22 +0530 Subject: [PATCH 039/207] Second type mapper is updated to use java.util.vector Second type mapper is updated to use java.util.vector --- .../java/com/iluwatar/datamapper/App.java | 31 ++++++++++++++++--- .../datamapper/StudentSecondDataMapper.java | 4 +-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java index 63e3af41dead..d23e0219d744 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -37,7 +37,8 @@ public final class App { private static Logger log = Logger.getLogger(App.class); - + private static final String DB_TYPE_FIRST = "first"; + private static final String DB_TYPE_SECOND = "second"; /** * Program entry point. @@ -46,10 +47,32 @@ public final class App { */ public static void main(final String... args) { + if (log.isInfoEnabled() & args.length > 0) { + log.debug("App.main(), type: " + args[0]); + } + + StudentDataMapper mapper = null; + + /* Check the desired db type from runtime arguments */ + if (args.length == 0) { + + /* Create default data mapper for mysql */ + mapper = new StudentFirstDataMapper(); - /* Create any type of mapper at implementation which is desired */ - /* final StudentDataMapper mapper = new StudentFirstDataMapper(); */ - final StudentDataMapper mapper = new StudentSecondDataMapper(); + } else if (args.length > 0 && DB_TYPE_FIRST.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for type 'first' */ + mapper = new StudentFirstDataMapper(); + + } else if (args.length > 0 && DB_TYPE_SECOND.equalsIgnoreCase(args[0])) { + + /* Create new data mapper for type 'second' */ + mapper = new StudentSecondDataMapper(); + } else { + + /* Don't couple any Data Mapper to java.sql.SQLException */ + throw new DataMapperException("Following data mapping type(" + args[0] + ") is not supported"); + } /* Create new student */ Student student = new Student(1, "Adam", 'A'); diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java index 62d39f90d4fe..7ad9788c0bce 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java @@ -18,14 +18,14 @@ */ package com.iluwatar.datamapper; -import java.util.ArrayList; +import java.util.Vector; import java.util.List; import java.util.Optional; public final class StudentSecondDataMapper implements StudentDataMapper { /* Note: Normally this would be in the form of an actual database */ - private List students = new ArrayList<>(); + private List students = new Vector<>(); @Override public Optional find(int studentId) { From c53dcf1274dcada25fd37bb0329240ba1c7cde3b Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Wed, 6 Apr 2016 13:14:10 +0530 Subject: [PATCH 040/207] Intent++ --- data-mapper/index.md | 51 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/data-mapper/index.md b/data-mapper/index.md index 684595c53c79..377487c8c4f4 100644 --- a/data-mapper/index.md +++ b/data-mapper/index.md @@ -1,26 +1,25 @@ ---- -layout: pattern -title: Data Mapper -folder: data-mapper -permalink: /patterns/dm/ -categories: Persistence Tier -tags: - - Java - - Difficulty-Beginner ---- - -## Intent -Object provides an abstract interface to some type of database or -other persistence mechanism. - -![alt text](./etc/data-mapper.png "Data Mapper") - -## Applicability -Use the Data Mapper in any of the following situations - -* when you want to consolidate how the data layer is accessed -* when you want to avoid writing multiple data retrieval/persistence layers - -## Credits - -* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/) +--- +layout: pattern +title: Data Mapper +folder: data-mapper +permalink: /patterns/dm/ +categories: Persistence Tier +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself + +![alt text](./etc/data-mapper.png "Data Mapper") + +## Applicability +Use the Data Mapper in any of the following situations + +* when you want to consolidate how the data layer is accessed +* when you want to avoid writing multiple data retrieval/persistence layers + +## Credits + +* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/) From 06e0a15400947ce703afea7e3c985c9efa8d73a0 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Wed, 6 Apr 2016 13:18:42 +0530 Subject: [PATCH 041/207] Applicability++ --- data-mapper/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data-mapper/index.md b/data-mapper/index.md index 377487c8c4f4..28b3939eef02 100644 --- a/data-mapper/index.md +++ b/data-mapper/index.md @@ -17,8 +17,8 @@ A layer of mappers that moves data between objects and a database while keeping ## Applicability Use the Data Mapper in any of the following situations -* when you want to consolidate how the data layer is accessed -* when you want to avoid writing multiple data retrieval/persistence layers +* when you want to decouple data objects from DB access layer +* when you want to write multiple data retrieval/persistence implementations ## Credits From b94a705161577f3b61a33c7bd0faa554a1417e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 15:14:12 +0300 Subject: [PATCH 042/207] Add constants for win sum and prize payer's bank account --- .../administration/LotteryAdministrationImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 93c68e82584b..af4e30227f97 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -45,6 +45,10 @@ */ public class LotteryAdministrationImpl implements LotteryAdministration { + private static final int WIN_AMOUNT = 100000; + + private static final String PRIZE_PAYER_BANK_ACCOUNT = "123-123"; + private final LotteryTicketRepository repository; private final LotteryService service = new LotteryServiceImpl(); @@ -69,8 +73,8 @@ public LotteryNumbers performLottery() { for (LotteryTicketId id: tickets.keySet()) { LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); if (result.equals(CheckResult.WIN_PRIZE)) { - bank.transferFunds(1000, "123-123", tickets.get(id).getPlayerDetails().getBankAccount()); - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), 1000); + bank.transferFunds(WIN_AMOUNT, PRIZE_PAYER_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), WIN_AMOUNT); } else if (result.equals(CheckResult.NO_PRIZE)) { notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); } From 48803d4c7d6333515b193dbf9a1d991e204d8fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 16:01:47 +0300 Subject: [PATCH 043/207] Add example lottery run --- .../main/java/com/iluwatar/hexagonal/App.java | 82 ++++++++++++++++++- .../LotteryAdministrationImpl.java | 4 +- .../LotteryNotificationsImpl.java | 2 +- .../hexagonal/service/LotteryServiceImpl.java | 6 +- 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 5554c36df414..58b84f2ee097 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -22,20 +22,98 @@ */ package com.iluwatar.hexagonal; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + /** * * Example application demonstrating Hexagonal Architecture * */ public class App { + + private static List allPlayerDetails; + + static { + allPlayerDetails = new ArrayList<>(); + allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("steve@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("andy@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("richard@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ian@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("robin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ted@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("larry@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("peter@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("warren@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("monica@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("lars@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("luke@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("anton@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ray@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ron@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "312-342", "+3242434242")); + } + /** * Program entry point */ public static void main(String[] args) { + // start new lottery round + LotteryAdministration administartion = new LotteryAdministrationImpl(); + administartion.resetLottery(); + // submit some lottery tickets + LotteryServiceImpl service = new LotteryServiceImpl(); + submitTickets(service, 20); - // perform lottery + int i = administartion.getAllSubmittedTickets().size(); - // check all the tickets + // perform lottery + administartion.performLottery(); + } + + private static void submitTickets(LotteryService lotteryService, int numTickets) { + for (int i=0; i tickets = getAllSubmittedTickets(); for (LotteryTicketId id: tickets.keySet()) { LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); - if (result.equals(CheckResult.WIN_PRIZE)) { + if (result.getResult().equals(CheckResult.WIN_PRIZE)) { bank.transferFunds(WIN_AMOUNT, PRIZE_PAYER_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); notifications.notifyPrize(tickets.get(id).getPlayerDetails(), WIN_AMOUNT); - } else if (result.equals(CheckResult.NO_PRIZE)) { + } else if (result.getResult().equals(CheckResult.NO_PRIZE)) { notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java index d91aacec07fd..c2e02ddbd442 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -41,7 +41,7 @@ public void notifyNoWin(PlayerDetails details) { @Override public void notifyPrize(PlayerDetails details, int prizeAmount) { System.out - .println(String.format("Lottery ticket for %s has won! Your bank account %s was deposited with %d credits.", + .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", details.getEmail(), details.getBankAccount(), prizeAmount)); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 7da7b2f1dce5..e595393df1fe 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -43,6 +43,10 @@ */ public class LotteryServiceImpl implements LotteryService { + private static final String LOTTERY_SERVICE_BANK_ACCOUNT = "123-123"; + + private static final int TICKET_PRIZE = 3; + private final LotteryTicketRepository repository; private final WireTransfers bank = new WireTransfersImpl(); @@ -55,7 +59,7 @@ public LotteryServiceImpl() { @Override public Optional submitTicket(LotteryTicket ticket) { - bank.transferFunds(3, ticket.getPlayerDetails().getBankAccount(), "123-123"); + bank.transferFunds(TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), LOTTERY_SERVICE_BANK_ACCOUNT); Optional optional = repository.save(ticket); if (optional.isPresent()) { notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); From 5de9c7e6b4a4783afab4f7da87153733bdb06fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 16:53:34 +0300 Subject: [PATCH 044/207] Lots of improvements to the example --- .../main/java/com/iluwatar/hexagonal/App.java | 88 ++++++++++--------- .../LotteryAdministrationImpl.java | 14 +-- .../hexagonal/banking/WireTransfersImpl.java | 4 +- .../hexagonal/domain/LotteryConstants.java | 38 ++++++++ .../domain/LotteryTicketCheckResult.java | 2 +- .../notifications/LotteryNotifications.java | 2 + .../LotteryNotificationsImpl.java | 14 +++ .../hexagonal/service/LotteryServiceImpl.java | 12 +-- .../hexagonal/lottery/LotteryTest.java | 14 ++- 9 files changed, 130 insertions(+), 58 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 58b84f2ee097..754f301b36fc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -28,6 +28,8 @@ import com.iluwatar.hexagonal.administration.LotteryAdministration; import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.PlayerDetails; @@ -46,44 +48,50 @@ public class App { static { allPlayerDetails = new ArrayList<>(); allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("steve@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("andy@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("richard@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ian@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("robin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ted@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("larry@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("peter@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("warren@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("monica@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("lars@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("luke@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("anton@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ray@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ron@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + allPlayerDetails.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + allPlayerDetails.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + allPlayerDetails.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + allPlayerDetails.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + allPlayerDetails.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + allPlayerDetails.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + allPlayerDetails.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + allPlayerDetails.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + allPlayerDetails.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + allPlayerDetails.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + allPlayerDetails.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + allPlayerDetails.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + allPlayerDetails.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + allPlayerDetails.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + allPlayerDetails.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + WireTransfersImpl wireTransfers = new WireTransfersImpl(); + Random random = new Random(); + for (int i = 0; i < allPlayerDetails.size(); i++) { + wireTransfers.setFunds(allPlayerDetails.get(i).getBankAccount(), + random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); + } } /** @@ -98,14 +106,12 @@ public static void main(String[] args) { LotteryServiceImpl service = new LotteryServiceImpl(); submitTickets(service, 20); - int i = administartion.getAllSubmittedTickets().size(); - // perform lottery administartion.performLottery(); } private static void submitTickets(LotteryService lotteryService, int numTickets) { - for (int i=0; i accounts = new HashMap<>(); static { - accounts.put("123-123", 50000); + accounts.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_SALDO); } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java new file mode 100644 index 000000000000..fb4c8025ff89 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Lottery domain constants + * + */ +public class LotteryConstants { + + public static final int PRIZE_AMOUNT = 100000; + public static final String SERVICE_BANK_ACCOUNT = "123-123"; + public static final int TICKET_PRIZE = 3; + public static final int SERVICE_BANK_ACCOUNT_SALDO = 150000; + public static final int PLAYER_MAX_SALDO = 100; + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java index d36dd28b70aa..534dc685f80a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -29,7 +29,7 @@ */ public class LotteryTicketCheckResult { - public enum CheckResult {WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED}; + public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED }; private final CheckResult checkResult; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java index c9917cb0731a..cc9f9d6e5bd2 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -32,7 +32,9 @@ public interface LotteryNotifications { void notifyTicketSubmitted(PlayerDetails details); + void notifyTicketSubmitError(PlayerDetails details); void notifyNoWin(PlayerDetails details); void notifyPrize(PlayerDetails details, int prizeAmount); + void notifyPrizeError(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java index c2e02ddbd442..c59a47970cf1 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -44,4 +44,18 @@ public void notifyPrize(PlayerDetails details, int prizeAmount) { .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", details.getEmail(), details.getBankAccount(), prizeAmount)); } + + @Override + public void notifyPrizeError(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", + details.getEmail(), prizeAmount)); + } + + @Override + public void notifyTicketSubmitError(PlayerDetails details) { + System.out.println( + String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", + details.getEmail())); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index e595393df1fe..d9a948597e6d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; @@ -43,10 +44,6 @@ */ public class LotteryServiceImpl implements LotteryService { - private static final String LOTTERY_SERVICE_BANK_ACCOUNT = "123-123"; - - private static final int TICKET_PRIZE = 3; - private final LotteryTicketRepository repository; private final WireTransfers bank = new WireTransfersImpl(); @@ -59,7 +56,12 @@ public LotteryServiceImpl() { @Override public Optional submitTicket(LotteryTicket ticket) { - bank.transferFunds(TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), LOTTERY_SERVICE_BANK_ACCOUNT); + boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), + LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } Optional optional = repository.save(ticket); if (optional.isPresent()) { notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java index 1da92023ddc6..0c5bb52168d3 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -35,13 +35,15 @@ import com.iluwatar.hexagonal.administration.LotteryAdministration; import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.service.LotteryService; import com.iluwatar.hexagonal.service.LotteryServiceImpl; import com.iluwatar.hexagonal.test.LotteryTestUtils; @@ -56,6 +58,7 @@ public class LotteryTest { private final LotteryAdministration admin = new LotteryAdministrationImpl(); private final LotteryService service = new LotteryServiceImpl(); private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private final WireTransfers wireTransfers = new WireTransfersImpl(); @Before public void clear() { @@ -65,6 +68,9 @@ public void clear() { @Test public void testLottery() { + // setup bank account with funds + wireTransfers.setFunds("123-12312", 100); + // admin resets the lottery admin.resetLottery(); assertEquals(admin.getAllSubmittedTickets().size(), 0); @@ -74,10 +80,10 @@ public void testLottery() { "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); assertTrue(ticket1.isPresent()); Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", - "123-12345", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); + "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); assertTrue(ticket2.isPresent()); Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", - "123-12367", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); + "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); assertTrue(ticket3.isPresent()); assertEquals(admin.getAllSubmittedTickets().size(), 3); @@ -86,7 +92,7 @@ public void testLottery() { // cheat a bit for testing sake, use winning numbers to submit another ticket Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", - "123-12399", "+12421255", winningNumbers.getNumbers())); + "123-12312", "+12421255", winningNumbers.getNumbers())); assertTrue(ticket4.isPresent()); assertEquals(admin.getAllSubmittedTickets().size(), 4); From 0fe8eec610d3cc4e0aa138267fbfff2a44a3504c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 17:17:29 +0300 Subject: [PATCH 045/207] Fix merge conflict --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index ef443a2d541b..e5515c51e88e 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,7 @@ value-object monad mute-idiom + hexagonal From 9b3aa785edcd5528eb89ea4198dc12f84b252e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 17:21:27 +0300 Subject: [PATCH 046/207] Add missing license headers --- .../com/iluwatar/dao/CustomerSchemaSql.java | 22 +++++++++++++++++++ .../com/iluwatar/dao/DbCustomerDaoTest.java | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java index 05707fa0eb85..860826abf095 100644 --- a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java +++ b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; public interface CustomerSchemaSql { diff --git a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java index 08e61ebe6448..7e6d7e1499ff 100644 --- a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java +++ b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; import static org.junit.Assert.assertEquals; From ccc1ec921a5a8c9bb47548d8b5c2d70579510943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 21:22:40 +0300 Subject: [PATCH 047/207] Add readme and class diagram --- hexagonal/README.md | 29 ++ hexagonal/etc/hexagonal.png | Bin 0 -> 165940 bytes hexagonal/etc/hexagonal.ucls | 325 ++++++++++++++++++ .../LotteryAdministrationImpl.java | 4 +- ...a => LotteryTicketInMemoryRepository.java} | 2 +- .../hexagonal/service/LotteryServiceImpl.java | 4 +- .../database/LotteryTicketRepositoryTest.java | 6 +- .../hexagonal/lottery/LotteryTest.java | 4 +- 8 files changed, 364 insertions(+), 10 deletions(-) create mode 100644 hexagonal/README.md create mode 100644 hexagonal/etc/hexagonal.png create mode 100644 hexagonal/etc/hexagonal.ucls rename hexagonal/src/main/java/com/iluwatar/hexagonal/database/{LotteryTicketRepositoryMock.java => LotteryTicketInMemoryRepository.java} (96%) diff --git a/hexagonal/README.md b/hexagonal/README.md new file mode 100644 index 000000000000..d49b21731a5f --- /dev/null +++ b/hexagonal/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Hexagonal Architecture +folder: hexagonal +permalink: /patterns/hexagonal/ +categories: Architectural +tags: + - Java + - Difficulty-Expert +--- + +## Intent +Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. + +![Hexagonal Architecture class diagram](./etc/hexagonal.png) + +## Applicability +Use Hexagonal Architecture pattern when + +* it is important that the application is fully testable +* you use Domain Driven Design methodology and/or Microservices architectural style + +## Real world examples + +* [Apache Isis](https://isis.apache.org/) + +## Credits + +* [Alistair Cockburn - Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) diff --git a/hexagonal/etc/hexagonal.png b/hexagonal/etc/hexagonal.png new file mode 100644 index 0000000000000000000000000000000000000000..8c03d375f998e2cd843e7a1dbc3726dc3720c8a0 GIT binary patch literal 165940 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

A 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 nubE?DSO9EB!|HX0hPM9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index c0c30f12df4a..5e457439bd5a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -27,7 +27,7 @@ import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; @@ -55,7 +55,7 @@ public class LotteryAdministrationImpl implements LotteryAdministration { private final WireTransfers bank = new WireTransfersImpl(); public LotteryAdministrationImpl() { - repository = new LotteryTicketRepositoryMock(); + repository = new LotteryTicketInMemoryRepository(); } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java similarity index 96% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java index 0629fb6db559..fe17d8cdf620 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java @@ -34,7 +34,7 @@ * Mock database for lottery tickets. * */ -public class LotteryTicketRepositoryMock implements LotteryTicketRepository { +public class LotteryTicketInMemoryRepository implements LotteryTicketRepository { private static Map tickets = new HashMap<>(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index d9a948597e6d..d446e93be025 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -27,7 +27,7 @@ import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; @@ -51,7 +51,7 @@ public class LotteryServiceImpl implements LotteryService { private final LotteryNotifications notifications = new LotteryNotificationsImpl(); public LotteryServiceImpl() { - repository = new LotteryTicketRepositoryMock(); + repository = new LotteryTicketInMemoryRepository(); } @Override diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java index a80e4817ac9b..b20e928c865b 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -31,7 +31,7 @@ import org.junit.Test; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.test.LotteryTestUtils; @@ -43,7 +43,7 @@ */ public class LotteryTicketRepositoryTest { - private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); @Before public void clear() { @@ -52,7 +52,7 @@ public void clear() { @Test public void testCrudOperations() { - LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); assertEquals(repository.findAll().size(), 0); LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); Optional id = repository.save(ticket); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java index 0c5bb52168d3..27e5bb6e471d 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -38,7 +38,7 @@ import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; @@ -57,7 +57,7 @@ public class LotteryTest { private final LotteryAdministration admin = new LotteryAdministrationImpl(); private final LotteryService service = new LotteryServiceImpl(); - private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); private final WireTransfers wireTransfers = new WireTransfersImpl(); @Before From c2f4194c07860c296666f6db1a3d4d85532f300a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 21:49:40 +0300 Subject: [PATCH 048/207] Add alias name for the pattern --- hexagonal/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hexagonal/README.md b/hexagonal/README.md index d49b21731a5f..defe5be57c51 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -9,6 +9,9 @@ tags: - Difficulty-Expert --- +## Also known as +Ports and Adapters + ## Intent Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. From e546d5dacc6966768617c96b703d90f5e302aa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 21:50:02 +0300 Subject: [PATCH 049/207] Add general description for the example code --- .../main/java/com/iluwatar/hexagonal/App.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 754f301b36fc..3ae55b706c25 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -38,7 +38,33 @@ /** * - * Example application demonstrating Hexagonal Architecture + * Hexagonal Architecture pattern decouples the application core from the + * services it uses. This allows the services to be plugged in and the + * application will run with or without the services.

+ * + * The core logic, or business logic, of an application consists of the + * algorithms that are essential to its purpose. They implement the use + * cases that are the heart of the application. When you change them, you + * change the essence of the application.

+ * + * The services are not essential. They can be replaced without changing + * the purpose of the application. Examples: database access and other + * types of storage, user interface components, e-mail and other + * communication components, hardware devices.

+ * + * This example demonstrates Hexagonal Architecture with a lottery system. + * The application core is separate from the services that drive it and + * from the services it uses.

+ * + * The primary ports for the application are {@link LotteryAdministration} + * through which the lottery round is initiated and run and + * {@link LotteryService} that allows players to submit lottery tickets for + * the draw.

+ * + * The secondary ports that application core uses are {@link WireTransfers} + * which is a banking service, {@link LotteryNotifications} that delivers + * notifications as lottery events occur and {@link LotteryTicketRepository} + * that is the storage for the lottery tickets. * */ public class App { From cfda338617ae2399a1f361f93b92be9b12c65422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 22:03:24 +0300 Subject: [PATCH 050/207] Fix version number --- hexagonal/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 1630e0eee7e7..540e6f398367 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.11.0-SNAPSHOT + 1.12.0-SNAPSHOT com.iluwatar.hexagonal hexagonal From a7dfe7681eae826660dc2175baacb0e2f06f6fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Mon, 11 Apr 2016 23:17:27 +0300 Subject: [PATCH 051/207] Add alias names --- hexagonal/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hexagonal/README.md b/hexagonal/README.md index defe5be57c51..5e954e90c819 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -11,6 +11,8 @@ tags: ## Also known as Ports and Adapters +Clean Architecture +Onion Architecture ## Intent Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. From 3f9a7566573473235947099b8a68d892f6aeed3f Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Wed, 13 Apr 2016 19:25:02 +0100 Subject: [PATCH 052/207] pom update --- pom.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 20b468246287..98b79de082f4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,19 +1,15 @@ @@ -50,6 +45,7 @@ 19.0 1.15.1 1.10.19 + 4.12.1 abstract-factory @@ -123,8 +119,7 @@ feature-toggle value-object monad - mutex - semaphore + mute-idiom @@ -197,6 +192,12 @@ ${systemrules.version} test + + de.bechte.junit + junit-hierarchicalcontextrunner + ${hierarchical-junit-runner-version} + test + @@ -396,4 +397,4 @@ - + \ No newline at end of file From ca8be7c43e3bc5a1590c5352137dcb2e203855b5 Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Wed, 13 Apr 2016 19:26:31 +0100 Subject: [PATCH 053/207] pom update --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 98b79de082f4..9d66f5f9b296 100644 --- a/pom.xml +++ b/pom.xml @@ -120,6 +120,8 @@ value-object monad mute-idiom + mutex + semaphore From 685d093cff803609c25d35e58bf663c55262c2fa Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Thu, 14 Apr 2016 17:33:52 +0100 Subject: [PATCH 054/207] added relative paths to pom in mutex/semaphore --- mutex/pom.xml | 1 + semaphore/pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/mutex/pom.xml b/mutex/pom.xml index 07c2d793cda1..6e418c1d1383 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -30,6 +30,7 @@ com.iluwatar java-design-patterns 1.11.0-SNAPSHOT + ../pom.xml mutex diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 14b551a1ae4f..2d0bfb907b9e 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -30,6 +30,7 @@ com.iluwatar java-design-patterns 1.11.0-SNAPSHOT + ../pom.xml semaphore From e821abdb1bc6e7a4238318bf89afb9cdb7810d95 Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Thu, 14 Apr 2016 17:44:18 +0100 Subject: [PATCH 055/207] updated version to fix pom --- mutex/pom.xml | 3 +-- semaphore/pom.xml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/mutex/pom.xml b/mutex/pom.xml index 6e418c1d1383..c9e0e83b7087 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,8 +29,7 @@ com.iluwatar java-design-patterns - 1.11.0-SNAPSHOT - ../pom.xml + 1.12.0-SNAPSHOT mutex diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 2d0bfb907b9e..a2dc143a1188 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,8 +29,7 @@ com.iluwatar java-design-patterns - 1.11.0-SNAPSHOT - ../pom.xml + 1.12.0-SNAPSHOT semaphore From a2843297d872df89c2d25a96bf12eb13a3852a28 Mon Sep 17 00:00:00 2001 From: gwildor28 Date: Sun, 17 Apr 2016 14:46:52 +0100 Subject: [PATCH 056/207] JUnit tests --- .../src/main/java/com/iluwatar/mutex/App.java | 2 +- .../src/main/java/com/iluwatar/mutex/Jar.java | 7 ++- .../main/java/com/iluwatar/mutex/Mutex.java | 13 ++++- .../main/java/com/iluwatar/mutex/Thief.java | 2 +- .../test/java/com/iluwatar/mutex/JarTest.java | 43 ++++++++++++++ .../java/com/iluwatar/mutex/MutexTest.java | 47 ++++++++++++++++ .../com/iluwatar/semaphore/Semaphore.java | 26 +++++++-- .../com/iluwatar/semaphore/FruitBowlTest.java | 51 +++++++++++++++++ .../com/iluwatar/semaphore/SemaphoreTest.java | 56 +++++++++++++++++++ 9 files changed, 236 insertions(+), 11 deletions(-) create mode 100644 mutex/src/test/java/com/iluwatar/mutex/JarTest.java create mode 100644 mutex/src/test/java/com/iluwatar/mutex/MutexTest.java create mode 100644 semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java create mode 100644 semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java diff --git a/mutex/src/main/java/com/iluwatar/mutex/App.java b/mutex/src/main/java/com/iluwatar/mutex/App.java index 5594535000fe..f6494bd5cbc5 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/App.java +++ b/mutex/src/main/java/com/iluwatar/mutex/App.java @@ -40,7 +40,7 @@ public class App { */ public static void main(String[] args) { Mutex mutex = new Mutex(); - Jar jar = new Jar(mutex); + Jar jar = new Jar(1000, mutex); Thief peter = new Thief("Peter", jar); Thief john = new Thief("John", jar); peter.start(); diff --git a/mutex/src/main/java/com/iluwatar/mutex/Jar.java b/mutex/src/main/java/com/iluwatar/mutex/Jar.java index e049b7a7589b..4fe0a4637065 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Jar.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Jar.java @@ -37,16 +37,17 @@ public class Jar { /** * The resource within the jar. */ - private int beans = 1000; + private int beans; - public Jar(Lock lock) { + public Jar(int beans, Lock lock) { + this.beans = beans; this.lock = lock; } /** * Method for a thief to take a bean. */ - public boolean takeBean(Thief thief) { + public boolean takeBean() { boolean success = false; try { lock.acquire(); diff --git a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java index e15deaf4805e..8e08534cdab6 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java @@ -32,6 +32,13 @@ public class Mutex implements Lock { */ private Object owner; + /** + * Returns the current owner of the Mutex, or null if available + */ + public Object getOwner() { + return owner; + } + /** * Method called by a thread to acquire the lock. If the lock has already * been acquired this will wait until the lock has been released to @@ -51,8 +58,10 @@ public synchronized void acquire() throws InterruptedException { */ @Override public synchronized void release() { - owner = null; - notify(); + if (Thread.currentThread() == owner) { + owner = null; + notify(); + } } } diff --git a/mutex/src/main/java/com/iluwatar/mutex/Thief.java b/mutex/src/main/java/com/iluwatar/mutex/Thief.java index 58ce019e0d77..d2225876c89a 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Thief.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Thief.java @@ -51,7 +51,7 @@ public Thief(String name, Jar jar) { public void run() { int beans = 0; - while (jar.takeBean(this)) { + while (jar.takeBean()) { beans = beans + 1; System.out.println(name + " took a bean."); } diff --git a/mutex/src/test/java/com/iluwatar/mutex/JarTest.java b/mutex/src/test/java/com/iluwatar/mutex/JarTest.java new file mode 100644 index 000000000000..97101466a16d --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/JarTest.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for taking beans from a Jar + */ +public class JarTest { + + @Test + public void testTakeBeans() { + Mutex mutex = new Mutex(); + Jar jar = new Jar(10, mutex); + for (int i = 0; i < 10; i++) { + assertTrue(jar.takeBean()); + } + assertFalse(jar.takeBean()); + } + +} \ No newline at end of file diff --git a/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java b/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java new file mode 100644 index 000000000000..8b3e82cb4db7 --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for acquiring and releasing a Mutex + */ +public class MutexTest { + + @Test + public void acquireReleaseTest() { + Mutex mutex = new Mutex(); + assertNull(mutex.getOwner()); + try { + mutex.acquire(); + assertEquals(mutex.getOwner(), Thread.currentThread()); + } catch (InterruptedException e) { + fail(e.toString()); + } + mutex.release(); + assertNull(mutex.getOwner()); + } + +} \ No newline at end of file diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java index 79f3b4465171..2e4a54c7c1ac 100644 --- a/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java @@ -27,13 +27,29 @@ */ public class Semaphore implements Lock { + private final int licenses; /** * The number of concurrent resource accesses which are allowed. */ private int counter; - public Semaphore(int counter) { - this.counter = counter; + public Semaphore(int licenses) { + this.licenses = licenses; + this.counter = licenses; + } + + /** + * Returns the number of licenses managed by the Semaphore + */ + public int getNumLicenses() { + return licenses; + } + + /** + * Returns the number of available licenses + */ + public int getAvailableLicenses() { + return counter; } /** @@ -52,8 +68,10 @@ public synchronized void acquire() throws InterruptedException { * Method called by a thread to release the lock. */ public synchronized void release() { - counter = counter + 1; - notify(); + if (counter < licenses) { + counter = counter + 1; + notify(); + } } } diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java new file mode 100644 index 000000000000..be31f77ab096 --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test taking from and putting Fruit into a FruitBowl + */ +public class FruitBowlTest { + + @Test + public void fruitBowlTest() { + FruitBowl fbowl = new FruitBowl(); + + assertEquals(fbowl.countFruit(), 0); + + for (int i = 1; i <= 10; i++) { + fbowl.put(new Fruit(Fruit.FruitType.LEMON)); + assertEquals(fbowl.countFruit(), i); + } + + for (int i = 9; i >= 0; i--) { + assertNotNull(fbowl.take()); + assertEquals(fbowl.countFruit(), i); + } + + assertNull(fbowl.take()); + } +} diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java new file mode 100644 index 000000000000..14587a485c4a --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for acquiring and releasing a Semaphore + */ +public class SemaphoreTest { + + @Test + public void acquireReleaseTest() { + Semaphore sphore = new Semaphore(3); + + assertEquals(sphore.getAvailableLicenses(), 3); + + for (int i = 2; i >= 0; i--) { + try { + sphore.acquire(); + assertEquals(sphore.getAvailableLicenses(), i); + } catch (InterruptedException e) { + fail(e.toString()); + } + } + + for (int i = 1; i <= 3; i++) { + sphore.release(); + assertEquals(sphore.getAvailableLicenses(), i); + } + + sphore.release(); + assertEquals(sphore.getAvailableLicenses(), 3); + } +} From 8529d6e34b962beaa53b6025975674547b633c47 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Mon, 18 Apr 2016 13:14:20 +0530 Subject: [PATCH 057/207] First review changes++ First review changes++ --- data-mapper/etc/data-mapper.png | Bin 54062 -> 40158 bytes data-mapper/pom.xml | 114 ++++++++---------- .../java/com/iluwatar/datamapper/App.java | 52 ++------ .../datamapper/DataMapperException.java | 2 + ...Mapper.java => StudentDataMapperImpl.java} | 8 +- .../datamapper/StudentSecondDataMapper.java | 101 ---------------- .../java/com/iluwatar/datamapper/AppTest.java | 1 - .../iluwatar/datamapper/DataMapperTest.java | 39 +----- .../com/iluwatar/datamapper/StudentTest.java | 50 ++++++++ 9 files changed, 118 insertions(+), 249 deletions(-) rename data-mapper/src/main/java/com/iluwatar/datamapper/{StudentFirstDataMapper.java => StudentDataMapperImpl.java} (92%) delete mode 100644 data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java create mode 100644 data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java diff --git a/data-mapper/etc/data-mapper.png b/data-mapper/etc/data-mapper.png index 8fc90f591deb0c08aa2479c37a268ef0b5d5de2c..bcda8054aac81eb6fb2bb564053a7e1fcd93c003 100644 GIT binary patch literal 40158 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≀G`|+$$&(X4Bxlnj6(385V$r3EXY7f;H#DF5Xf7 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 literal 54062 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 diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 866f5b76e3a7..76ba00f944c1 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -23,71 +23,61 @@ THE SOFTWARE. --> - - 4.0.0 - - com.iluwatar - java-design-patterns - 1.11.0-SNAPSHOT - - data-mapper + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + data-mapper + + + junit + junit + test + + + log4j + log4j + + - - - junit - junit - test - - - log4j - log4j - - + - + + + + src/main/resources + + + src/main/resources + + log4j.xml + + .. + + - - - - - src/main/resources - - - src/main/resources - - log4j.xml - - .. - - + - + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + log4j.xml + + + + true + + + + - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - log4j.xml - - - - true - - - - - - - + + diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java index d23e0219d744..5fcd0d9ea04c 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -1,5 +1,5 @@ /** - * The MIT License Copyright (c) 2014 Ilkka Seppälä + * The MIT License Copyright (c) 2016 Amit Dixit * * 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, @@ -37,9 +37,6 @@ public final class App { private static Logger log = Logger.getLogger(App.class); - private static final String DB_TYPE_FIRST = "first"; - private static final String DB_TYPE_SECOND = "second"; - /** * Program entry point. * @@ -47,49 +44,21 @@ public final class App { */ public static void main(final String... args) { - if (log.isInfoEnabled() & args.length > 0) { - log.debug("App.main(), type: " + args[0]); - } - - StudentDataMapper mapper = null; - - /* Check the desired db type from runtime arguments */ - if (args.length == 0) { - - /* Create default data mapper for mysql */ - mapper = new StudentFirstDataMapper(); - - } else if (args.length > 0 && DB_TYPE_FIRST.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for type 'first' */ - mapper = new StudentFirstDataMapper(); - - } else if (args.length > 0 && DB_TYPE_SECOND.equalsIgnoreCase(args[0])) { - - /* Create new data mapper for type 'second' */ - mapper = new StudentSecondDataMapper(); - } else { - - /* Don't couple any Data Mapper to java.sql.SQLException */ - throw new DataMapperException("Following data mapping type(" + args[0] + ") is not supported"); - } + /* Create new data mapper for type 'first' */ + final StudentDataMapper mapper = new StudentDataMapperImpl(); /* Create new student */ Student student = new Student(1, "Adam", 'A'); - /* Add student in respectibe db */ + /* Add student in respectibe store */ mapper.insert(student); - if (log.isDebugEnabled()) { - log.debug("App.main(), student : " + student + ", is inserted"); - } + log.debug("App.main(), student : " + student + ", is inserted"); /* Find this student */ final Optional studentToBeFound = mapper.find(student.getStudentId()); - if (log.isDebugEnabled()) { - log.debug("App.main(), student : " + studentToBeFound + ", is searched"); - } + log.debug("App.main(), student : " + studentToBeFound + ", is searched"); /* Update existing student object */ student = new Student(student.getStudentId(), "AdamUpdated", 'A'); @@ -97,15 +66,10 @@ public static void main(final String... args) { /* Update student in respectibe db */ mapper.update(student); - if (log.isDebugEnabled()) { - log.debug("App.main(), student : " + student + ", is updated"); - } + log.debug("App.main(), student : " + student + ", is updated"); + log.debug("App.main(), student : " + student + ", is going to be deleted"); /* Delete student in db */ - - if (log.isDebugEnabled()) { - log.debug("App.main(), student : " + student + ", is deleted"); - } mapper.delete(student); } diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java index 7cc66d5af5b2..24a7cf081cbf 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java @@ -19,6 +19,8 @@ package com.iluwatar.datamapper; /** + * Using Runtime Exception for avoiding dependancy on implementation exceptions. This helps in + * decoupling. * * @author amit.dixit * diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentFirstDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java similarity index 92% rename from data-mapper/src/main/java/com/iluwatar/datamapper/StudentFirstDataMapper.java rename to data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java index 97f6d395d6de..7ecd9e7f84d6 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentFirstDataMapper.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.Optional; -public final class StudentFirstDataMapper implements StudentDataMapper { +public final class StudentDataMapperImpl implements StudentDataMapper { /* Note: Normally this would be in the form of an actual database */ private List students = new ArrayList<>(); @@ -59,7 +59,7 @@ public void update(Student studentToBeUpdated) throws DataMapperException { } else { - /* Throw user error */ + /* Throw user error after wrapping in a runtime exception */ throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); } } @@ -75,7 +75,7 @@ public void insert(Student studentToBeInserted) throws DataMapperException { } else { - /* Throw user error */ + /* Throw user error after wrapping in a runtime exception */ throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); } } @@ -91,7 +91,7 @@ public void delete(Student studentToBeDeleted) throws DataMapperException { } else { - /* Throw user error */ + /* Throw user error after wrapping in a runtime exception */ throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); } } diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java deleted file mode 100644 index 7ad9788c0bce..000000000000 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentSecondDataMapper.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * 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.datamapper; - -import java.util.Vector; -import java.util.List; -import java.util.Optional; - -public final class StudentSecondDataMapper implements StudentDataMapper { - - /* Note: Normally this would be in the form of an actual database */ - private List students = new Vector<>(); - - @Override - public Optional find(int studentId) { - - /* Compare with existing students */ - for (final Student student : this.getStudents()) { - - /* Check if student is found */ - if (student.getStudentId() == studentId) { - - return Optional.of(student); - } - } - - /* Return empty value */ - return Optional.empty(); - } - - @Override - public void update(Student studentToBeUpdated) throws DataMapperException { - - /* Check with existing students */ - if (this.getStudents().contains(studentToBeUpdated)) { - - /* Get the index of student in list */ - final int index = this.getStudents().indexOf(studentToBeUpdated); - - /* Update the student in list */ - this.getStudents().set(index, studentToBeUpdated); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); - } - } - - @Override - public void insert(Student studentToBeInserted) throws DataMapperException { - - /* Check with existing students */ - if (!this.getStudents().contains(studentToBeInserted)) { - - /* Add student in list */ - this.getStudents().add(studentToBeInserted); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); - } - } - - @Override - public void delete(Student studentToBeDeleted) throws DataMapperException { - - /* Check with existing students */ - if (this.getStudents().contains(studentToBeDeleted)) { - - /* Delete the student from list */ - this.getStudents().remove(studentToBeDeleted); - - } else { - - /* Throw user error */ - throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); - } - } - - public List getStudents() { - return this.students; - } -} diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java index d3ad2dcb392e..f2858100efbb 100644 --- a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java @@ -1,6 +1,5 @@ /** * The MIT License Copyright (c) 2016 Amit Dixit - * * * 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, diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java index f77097df8663..17f4d3922447 100644 --- a/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java @@ -24,8 +24,7 @@ import com.iluwatar.datamapper.Student; import com.iluwatar.datamapper.StudentDataMapper; -import com.iluwatar.datamapper.StudentFirstDataMapper; -import com.iluwatar.datamapper.StudentSecondDataMapper; +import com.iluwatar.datamapper.StudentDataMapperImpl; /** * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the @@ -45,41 +44,7 @@ public class DataMapperTest { public void testFirstDataMapper() { /* Create new data mapper of first type */ - final StudentDataMapper mapper = new StudentFirstDataMapper(); - - /* Create new student */ - Student student = new Student(1, "Adam", 'A'); - - /* Add student in respectibe db */ - mapper.insert(student); - - /* Check if student is added in db */ - assertEquals(student.getStudentId(), mapper.find(student.getStudentId()).get().getStudentId()); - - /* Update existing student object */ - student = new Student(student.getStudentId(), "AdamUpdated", 'A'); - - /* Update student in respectibe db */ - mapper.update(student); - - /* Check if student is updated in db */ - assertEquals(mapper.find(student.getStudentId()).get().getName(), "AdamUpdated"); - - /* Delete student in db */ - mapper.delete(student); - - /* Result should be false */ - assertEquals(false, mapper.find(student.getStudentId()).isPresent()); - } - - /** - * This test verify that second data mapper is able to perform all CRUD operations on Student - */ - @Test - public void testSecondDataMapper() { - - /* Create new data mapper of second type */ - final StudentDataMapper mapper = new StudentSecondDataMapper(); + final StudentDataMapper mapper = new StudentDataMapperImpl(); /* Create new student */ Student student = new Student(1, "Adam", 'A'); diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java new file mode 100644 index 000000000000..c441d6d6e173 --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +public final class StudentTest { + + @Test + /** + * This API tests the equality behaviour of Student object + * Object Equality should work as per logic defined in equals method + * @throws Exception + */ + public void testEquality() throws Exception { + + /* Create some students */ + final Student firstStudent = new Student(1, "Adam", 'A'); + final Student secondStudent = new Student(2, "Donald", 'B'); + final Student secondSameStudent = new Student(2, "Donald", 'B'); + final Student firstSameStudent = firstStudent; + + /* Check equals functionality: should return 'true' */ + assertTrue(firstStudent.equals(firstSameStudent)); + + /* Check equals functionality: should return 'false' */ + assertFalse(firstStudent.equals(secondStudent)); + + /* Check equals functionality: should return 'true' */ + assertTrue(secondStudent.equals(secondSameStudent)); + } +} From 32736fc90c48d0b1ea78841abf0f937eec444935 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Mon, 18 Apr 2016 17:05:18 +0530 Subject: [PATCH 058/207] uml diagram++ --- data-mapper/etc/data-mapper.ucls | 68 +++++++++++--------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/data-mapper/etc/data-mapper.ucls b/data-mapper/etc/data-mapper.ucls index ff10fc32fe57..2467983ce212 100644 --- a/data-mapper/etc/data-mapper.ucls +++ b/data-mapper/etc/data-mapper.ucls @@ -3,8 +3,8 @@ associations="true" dependencies="false" nesting-relationships="true" router="FAN"> - - + @@ -13,78 +13,56 @@ - + - - + - - - - - - - - - + - - - - - - - - - + + + + + + + + + - - + + - - - - - - - - - + - - - - - - + + - + From 28c271486274c0e9aca866a90c28f630524c5d48 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Tue, 19 Apr 2016 11:25:42 +0530 Subject: [PATCH 059/207] review comments++ review comments++ --- .../datamapper/DataMapperException.java | 39 ------------------- pom.xml | 1 + 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java index 24a7cf081cbf..a6995b06d6fe 100644 --- a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java @@ -29,15 +29,6 @@ public final class DataMapperException extends RuntimeException { private static final long serialVersionUID = 1L; - /** - * Constructs a new runtime exception with {@code null} as its detail message. The cause is not - * initialized, and may subsequently be initialized by a call to {@link #initCause}. - */ - public DataMapperException() { - super(); - } - - /** * Constructs a new runtime exception with the specified detail message. The cause is not * initialized, and may subsequently be initialized by a call to {@link #initCause}. @@ -48,34 +39,4 @@ public DataMapperException() { public DataMapperException(final String message) { super(message); } - - /** - * Constructs a new runtime exception with the specified detail message and cause. - *

- * Note that the detail message associated with {@code cause} is not automatically - * incorporated in this runtime exception's detail message. - * - * @param message the detail message (which is saved for later retrieval by the - * {@link #getMessage()} method). - * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). - * (A null value is permitted, and indicates that the cause is nonexistent or - * unknown.) - */ - public DataMapperException(final String message, final Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new runtime exception with the specified cause and a detail message of - * (cause==null ? null : cause.toString()) (which typically contains the class and detail - * message of cause). This constructor is useful for runtime exceptions that are little - * more than wrappers for other throwables. - * - * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). - * (A null value is permitted, and indicates that the cause is nonexistent or - * unknown.) - */ - public DataMapperException(final Throwable cause) { - super(cause); - } } diff --git a/pom.xml b/pom.xml index 5558446601c8..6e0b14170000 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ bridge composite dao + data-mapper decorator facade flyweight From 8fa774d420c350a0466608731428ad86bfd0a36b Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Tue, 19 Apr 2016 11:35:13 +0530 Subject: [PATCH 060/207] mvn build file++ --- data-mapper/pom.xml | 128 ++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 83 deletions(-) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 76ba00f944c1..7474fe5380b4 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -1,83 +1,45 @@ - - - - 4.0.0 - - com.iluwatar - java-design-patterns - 1.11.0-SNAPSHOT - - data-mapper - - - junit - junit - test - - - log4j - log4j - - - - - - - - - src/main/resources - - - src/main/resources - - log4j.xml - - .. - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - log4j.xml - - - - true - - - - - - - - + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + data-mapper + + + junit + junit + test + + + log4j + log4j + + + From d5f7cb4e2c783a809a3e4bc1e24ec4785ee31372 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Tue, 19 Apr 2016 11:49:35 +0530 Subject: [PATCH 061/207] build check style error-- --- .gitignore | 7 ++++++- .../src/test/java/com/iluwatar/datamapper/StudentTest.java | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5d2cd77e176a..589d3fb13b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,9 @@ target .idea *.iml *.swp -datanucleus.log \ No newline at end of file +datanucleus.log +/bin/ +/bin/ +/bin/ + +data-mapper/src/main/resources/log4j.xml \ No newline at end of file diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java index c441d6d6e173..a3c0e46c1dc6 100644 --- a/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java @@ -28,7 +28,8 @@ public final class StudentTest { /** * This API tests the equality behaviour of Student object * Object Equality should work as per logic defined in equals method - * @throws Exception + * + * @throws Exception if any execution error during test */ public void testEquality() throws Exception { From 93b0b3730e57fbd29efea3c8fd2aeff6220790be Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Tue, 19 Apr 2016 11:59:27 +0530 Subject: [PATCH 062/207] build error fix++ --- data-mapper/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 7474fe5380b4..82e5f531df2d 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -29,6 +29,7 @@ com.iluwatar java-design-patterns 1.11.0-SNAPSHOT + data-mapper From 018a4e52a2fdbd0274d74f84b5141228ba439efb Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Tue, 19 Apr 2016 12:23:52 +0530 Subject: [PATCH 063/207] build error fix++ --- data-mapper/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 82e5f531df2d..0ec95d4687da 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns 1.11.0-SNAPSHOT - + ../pom.xml data-mapper From 59e9d027d42ee9742566ac8cbd4e8922c418986f Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Tue, 19 Apr 2016 12:45:12 +0530 Subject: [PATCH 064/207] version++ --- data-mapper/pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 0ec95d4687da..da646f3c1579 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,8 +28,7 @@ com.iluwatar java-design-patterns - 1.11.0-SNAPSHOT - ../pom.xml + 1.7.0 data-mapper From af1db79aa7e06ba8479eac8d2a30e97149755b4a Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Wed, 20 Apr 2016 07:23:12 +0530 Subject: [PATCH 065/207] version++ --- data-mapper/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index da646f3c1579..f65b647fa939 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.7.0 + 1.12.0-SNAPSHOT data-mapper From bf7b6825496d71ca69d3f26854f968392d1ad005 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Sun, 24 Apr 2016 12:25:05 +0200 Subject: [PATCH 066/207] Add License Shield --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8db718edbf72..811d6a17ab71 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns) [![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) +[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Introduction From 71e3443e0ea28a6c8e2b94c2e4cc6c0bf24dc7a2 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Sun, 24 Apr 2016 12:58:38 +0200 Subject: [PATCH 067/207] Fix permalink to represent its current dir Otherwise the picture cant be found, as the jekyll build process will put this file in a 'dm' directory and the other stuff like resources in a 'data-mapper' directory (because resources dont have permalink specified they are served static) --- data-mapper/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-mapper/index.md b/data-mapper/index.md index 28b3939eef02..075e8eecef15 100644 --- a/data-mapper/index.md +++ b/data-mapper/index.md @@ -2,7 +2,7 @@ layout: pattern title: Data Mapper folder: data-mapper -permalink: /patterns/dm/ +permalink: /patterns/data-mapper/ categories: Persistence Tier tags: - Java From a54cea777fd26868443e7a8c88ffd57997cc1bf7 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sun, 24 Apr 2016 15:24:33 -0500 Subject: [PATCH 068/207] #297 Create class diagrams for API Gateway, Price microservice, and Image microservice --- api-gateway/etc/api-gateway.png | Bin 0 -> 18323 bytes api-gateway/etc/api-gateway.ucls | 97 ++++++++++++++++++ .../etc/image-microservice.png | Bin 0 -> 2695 bytes .../etc/image-microservice.ucls | 20 ++++ .../etc/price-microservice.png | Bin 0 -> 2572 bytes .../etc/price-microservice.ucls | 20 ++++ 6 files changed, 137 insertions(+) create mode 100644 api-gateway/etc/api-gateway.png create mode 100644 api-gateway/etc/api-gateway.ucls create mode 100644 api-gateway/image-microservice/etc/image-microservice.png create mode 100644 api-gateway/image-microservice/etc/image-microservice.ucls create mode 100644 api-gateway/price-microservice/etc/price-microservice.png create mode 100644 api-gateway/price-microservice/etc/price-microservice.ucls diff --git a/api-gateway/etc/api-gateway.png b/api-gateway/etc/api-gateway.png new file mode 100644 index 0000000000000000000000000000000000000000..bb3ec2e2e1a824699a539c89f362d2573e3860c9 GIT binary patch literal 18323 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/etc/image-microservice.png b/api-gateway/image-microservice/etc/image-microservice.png new file mode 100644 index 0000000000000000000000000000000000000000..340285a14f64aac47061b4c74373476f8fcc74fd GIT binary patch literal 2695 zcmV;23V8L2P) z*^d-O9LFayKAP|k_@FVq2}G2QF2e&W^1+$s3};&59RW&M$ILcnqjGh3`>1vSn4Cg zQXe%5%TjkX+g++nY$L_2|KMlYHIKZJYBiP)NFQOjU}f5hCBw4D!d0vaLDgLu;<1%Vg%V;Zoy9DeBq4>^xr}ff$RVx566(>Z8P`67LNEgc1FR}% zb0JXCf}N3La77DrX$DU2(3CkfOOH@;ow`~!Gl#_H!G)ws` z`c;lrj>O3ZHQ&GxZCRq)iEWnv!#47#d(J`^ zzHQ-lPkoH|D}8?PFBHXDHapbEBZs6%sb^^vTZW}RGA#9xVX2P{OMPTm>Z4>?Ub}I1 z$-(tA_jJDW$($L3OJN^E8eV2yl_1OB(LI3ho$uFf8vei&>`&^Q1>p@Zww6kYB>;QA zXIg%A>+u`MF5R)O{C*ie4_!UbGurda#&!;;+Z#}~?k@Iczq&+muWzJi(dtshL|8&Y z3KqF}c+XhME#8(II|soee>`pWr4F+E=xMeY%-vTM<}UPNr7!y=S*=ZDucV= zgDO-$*bSkXanoXhml@jUmu+Js0U;^kq!nX0QdH?-*)gzmZei)cE8jv?ckbWqJovV! zH9xFdd3Y6Q(a>@@M#(aRhlO1Wa9c$(RLRD$c_v)L}qR57_Y{i%y+wvL+b|&SgPwt!1-amU% zex}+k>3BW4kVOWhx1EhPRG(%8XUqbl2G~U*0i$ z)9?<6>W>G1PT4uPea9To;cLUku8%G(u9-HlFvu>$So*D#Q>||OiY(oI%97js5i6Bl z^5LXf$HJ2Sq-H5*EY&RGUfAA0XK8WO!$>j4PU&d#tjwJMci87XAmZ1E}!SP!zdJArtVIqg9kFOQU+qu+&F}r9Lt&^^swzj|@wFlsrp2=k;b)IZLKA66^nB)$vzd zqV%OwFT1R=`~|Cp|NB>}I4o(u7vGUaChL#^+nnw9PKi?)Grn3;xLgvc5`hQK2k_YAK;(gdH_1AqlVfxfie|E?eozJ*$`%A@^J>S6sm<~iK`#vNWkAO$zV0+&CMcJrXo)s zt(2u^66~_!`Kgs!k!ASPVDqXqS?VdnQXd(X`pB@J9mTUxA}m#FH^n(IW@aj4iC>9V&$$mZXF2P;_m>^De!BVfzAJ;U z17F1kmeotnaxKYmg7tR-7HA?~;YH*f${nLzy9q_b91q=949JPR=ewUD?gc=cUnI8* zijX=xmwCYY7ui_fm(>`S>rZ_?|H!&y*N2Gq!B+?rFI#`s0DZX%TAE3*KohmIf<<4P zg4U2wJF0sL2{CXX{0dR%{cGwIY2M`2@ww8vQi1NZDQEduPqz0$Z{Ni}Uv?jSg+Mh+ zVFj*`rIuuJXcK((nK%c2;UbXM_QIAObRt_oh)-Qufl6ApDkaTqWh|fEHGjp?b?Z-T zbhQCK;VT4Iz*1gnDUKB??_|lb3`t!ZmR-Z!r|etSe>UHLCcpDc9(Jbe?S#O}Sc(fY zsoGfNBTpxUDAsNYyevXlMz*m+>Ks3@jZKfkbj8m;uNGI&IPliq3;n%6e*`=5RXir5 zja4nsM4o*tOL{q#yt0$(b+Xb5vxKGpYF61vSn4CgQXd(X`lwMX=}VXr7`Tr0 z*6xT5MDzwI-+wP3nUsq~?O5`D1=Ut}qAc-{h%)~0421X6v95V(bhcjDYQs|HS0Ei0 z;U^#r_b2IuK=%1~l>P=8@pWc?acJ+kG`?NN$H)=pY+5?b+aaJ7b!HjMuON<}fM6E2 zbwZHsQjSOAZ-A4C@Eky^l&B0HJ23dA6JnN1QGb>d_!Z#F4H>CV!U;inzYK~eWXKb- z4CwUdS1M8tlyX;+B8eIfe^$n_0>1*Gx`RgQ>*ItF63>`eqF-5zjvA<#AOG z41p|FrKms4as_?`iTVj78F~FY2WnZ0YFI`oQ9pq?IUywMIgm&xl3;1n7#WuO$gtE$ zhNV6-EcKC^rFmCVGj8g4HJPp1H^Wk&&9KyG`yYfYFS4V)%1ZzM002ovPDHLkV1h34 BF%JL$ literal 0 HcmV?d00001 diff --git a/api-gateway/image-microservice/etc/image-microservice.ucls b/api-gateway/image-microservice/etc/image-microservice.ucls new file mode 100644 index 000000000000..d3520768f851 --- /dev/null +++ b/api-gateway/image-microservice/etc/image-microservice.ucls @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/price-microservice/etc/price-microservice.png b/api-gateway/price-microservice/etc/price-microservice.png new file mode 100644 index 0000000000000000000000000000000000000000..7c57160257cbaed699937c23699d8a4cd4b8896d GIT binary patch literal 2572 zcmV+n3iI`eP) z?QaxC9LFy)zBJ(<@P)?sP9UN*#-tD*%27ff4Jn0_Qm~3v2_-I6Dqyb`TeQ-O@o3@1 zibf11El}klKsSL18be|b-|%w%?bFO}9`jo=whUW~Gc+Lpr13m9kD{E~tJp=?1^yq};FZ z^pxfQ%tVX>FS^t$Jg7LkDx4M&XD?FGg@=c#h5$AVpBVuo_{j}V3`M?* z(Tb7S*&q!x(4}_Zq3Q_T@}Ty;OWwa@`ADFqyenPmN4y9)$DmL-8&-X`Jfy&To`(d( zbFX^4u1OWAjCcZMT9l>WheqqS27u(86L@SWyBqLZnA?U#m-@cv<&<5O_$sz*-1(j` z(4}n}xGR(U;eREA4)4m4gfgjDkB8_I1w|E}RtRZ$3QNONSQ?(f((n|PhNrN~3-8*E ztLqMLUADjDrB7EjkG8`(xKzE&HKQ`Z>!02a%-;E7OV8wouHbw@|8j7zda-Lm<$?!< zp6^?doZfZf#_>yc%qzcNhR=zshx(@bp4r~W@}qyB*|~gH&foR|W}ZwGdm$57c`sN8ZzxN|x4g@Dug%@{`g1#91Hs9HCH<<+W3}-4#tuoz;mBkkilaP&y*u;d91siN-^-rPjYyC#v%<5 z?q`idE8Eju42C8aG7snC{36mZY0TFVP6 zTBP8?ND}@JtU9n^$7J&M{ktUEzaIWQeBrCM@lNiJ*qEpv`Ozn(tBQb zc=AZH-t$n#A`B0dJT*FZXH(O@)g2S>?mRWH=jfL}vghC%R{(I)4C z?RaOI5u@yp1xf2+;E}(H@KwlX$kC=GW6_4^@|(A-sU@|s^Rov06yW+_!(cv@wZhNrMJJcXsGATYDGWR^!-OUbWB!~WQ1J|y3DcVg;@8H} zmgghPq#Fzq;_<9*-bA~*T8>UQp@g`JD2ONCz$ApTVIdD*id+$VJBF}WL|ztYBs33f zg-|02Hr=7>@@|1_KQGf9hv?hs4TKbND^FypyE$uGP8VIDChk8 z!o0E?5*`P`OVaXFMTucBVc$D}=T@+nrAf=bVc+udL?X@|-nMrqJ>VfakA>1U3zZonz=s?5=}U1oB|RxJWX{4|gJG!zE$Oit7vW$ZA-4lz+fIPY%y-G)$hns}lNsvJ%eg z2I&e|=OQ!E4ZvH!iri@xpj6@w!d(it57~``*tN5lMH&gs15+W$bk5%Md}}QR%&adA zmDT*=DWJ-X<|8HzPhr&xyt?Di+@2(`#I8 zBljlYr0V6VE0qSGMUUziseYv4DXcPdn|RH!xRyS@eME#kWI`$2qq_XQ3OU3)Us`Q! zg~Qu+X7uso(z#!Ez=>qQf~%vbcb?1kkO>noB#*q{;Wv;)=8=oM;Vu9EgZ0OZpKpFM zc4ZV!;49x}aywag6_haC*9u9tqF~-}20fmI9@I=2G$8w&HAz<*5W|zC zPQ%iNGrg;-*q0Yo0p8ZrU$h?WK7M`N*BE>SNB$z^TAs}bJ(=pg$IEl^9#2N0=<|Tm zZAiF1EEpGdry4F@F(82_rjEl3``hqOV=4jfvA%f!#r}br0Z--td<93Le%X7Tye$fw zN}z*#GMw>-UUbE5oFQ&1#9c#PfpYRnN zd3YR{knpl@y;XL2SyHbJc%74b7LBbRI+q+eo7{Ug2`7sVbbw>t@VG63n68zKQv&vo zk@kK$;pOZ$xzw3b@ZBcez(^r@pjXrHHy?WIz{R2dUp|Ht_{#eU1uw*a#b?HhGSIj~ z*nrZ9=e^C6Z{PBO*#;~$Y%L5}Tx~h|nfq4(PO4sj3aK{!D%7oNRQOJnW@t z2dyB>ACstHCREg(m#-x~@M#ojtqH-4F}r6sc6-0p6}(WEbZNI4WT!5f**%xB+itAH zL)L5r!Bf54w8B`@EqY@&NDcDi3%Rj70A48uEbKOI`00w^aT5nmRxY*RS$SB}QL!ta z${D*WTdvZ=%cX2@87!;HZ0xRDxhe@yt3uN76qbgkurxe{rQs + + + + + + + + + + + + + + \ No newline at end of file From 28d259a55c8cac1de4fff75185e63c77e962fc1d Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sun, 24 Apr 2016 16:00:39 -0500 Subject: [PATCH 069/207] #297 Create README.md --- api-gateway/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 api-gateway/README.md diff --git a/api-gateway/README.md b/api-gateway/README.md new file mode 100644 index 000000000000..069cdad5792f --- /dev/null +++ b/api-gateway/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: API Gateway +folder: api-gateway +permalink: /patterns/api-gateway/ +categories: Other +tags: +- Java +- Difficulty-Beginner + +## 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. + +![alt text](./etc/api-gateway.png "API Gateway") + +## 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/) From b92318ae36e32f9648bb15f3115fbad798732607 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sun, 24 Apr 2016 16:00:53 -0500 Subject: [PATCH 070/207] #297 Increase version number --- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index dadedcdae4f0..c55b8026c30a 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -6,7 +6,7 @@ ../../pom.xml java-design-patterns com.iluwatar - 1.10.0-SNAPSHOT + 1.12.0-SNAPSHOT 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 36eef6668feb..75c34d5c5ea0 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -5,7 +5,7 @@ java-design-patterns com.iluwatar - 1.10.0-SNAPSHOT + 1.12.0-SNAPSHOT 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index ad5cb9ee6d97..d9634480ecec 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -6,7 +6,7 @@ ../../pom.xml java-design-patterns com.iluwatar - 1.10.0-SNAPSHOT + 1.12.0-SNAPSHOT 4.0.0 From b115c618737600c637379e3df116bc146dccb09d Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sun, 24 Apr 2016 16:05:38 -0500 Subject: [PATCH 071/207] #297 Fix error in README --- api-gateway/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/api-gateway/README.md b/api-gateway/README.md index 069cdad5792f..963f34761bb2 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -7,6 +7,7 @@ categories: Other tags: - Java - Difficulty-Beginner +--- ## Intent From 5abd20f9e17da9dfbf3330489add100416c991b3 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Tue, 3 May 2016 16:28:20 -0500 Subject: [PATCH 072/207] #297 Update license headers --- api-gateway/image-microservice/pom.xml | 24 +++++++++++++++++++ .../image/microservice/ImageApplication.java | 22 +++++++++++++++++ .../image/microservice/ImageController.java | 22 +++++++++++++++++ .../src/main/resources/application.properties | 23 ++++++++++++++++++ .../microservice/ImageControllerTest.java | 22 +++++++++++++++++ api-gateway/pom.xml | 24 +++++++++++++++++++ api-gateway/price-microservice/pom.xml | 24 +++++++++++++++++++ .../price/microservice/PriceApplication.java | 22 +++++++++++++++++ .../price/microservice/PriceController.java | 22 +++++++++++++++++ .../src/main/resources/application.properties | 23 ++++++++++++++++++ .../microservice/PriceControllerTest.java | 22 +++++++++++++++++ .../com/iluwatar/api/gateway/ApiGateway.java | 22 +++++++++++++++++ .../java/com/iluwatar/api/gateway/App.java | 22 +++++++++++++++++ .../iluwatar/api/gateway/DesktopProduct.java | 22 +++++++++++++++++ .../com/iluwatar/api/gateway/ImageClient.java | 22 +++++++++++++++++ .../iluwatar/api/gateway/ImageClientImpl.java | 22 +++++++++++++++++ .../iluwatar/api/gateway/MobileProduct.java | 22 +++++++++++++++++ .../com/iluwatar/api/gateway/PriceClient.java | 22 +++++++++++++++++ .../iluwatar/api/gateway/PriceClientImpl.java | 22 +++++++++++++++++ .../src/main/resources/application.properties | 23 ++++++++++++++++++ .../iluwatar/api/gateway/ApiGatewayTest.java | 22 +++++++++++++++++ 21 files changed, 471 insertions(+) diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index c55b8026c30a..63fe074121e2 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -1,4 +1,28 @@ + diff --git a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java index 8a346d7626e9..b4cb3d25041b 100644 --- a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.boot.SpringApplication; 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 index b98683600c32..16faf5b70a2b 100644 --- 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 @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/image-microservice/src/main/resources/application.properties b/api-gateway/image-microservice/src/main/resources/application.properties index 40e46c1ce547..aeda0c24cab9 100644 --- a/api-gateway/image-microservice/src/main/resources/application.properties +++ b/api-gateway/image-microservice/src/main/resources/application.properties @@ -1 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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 index bac3e2ae0efc..cf0c099c1f01 100644 --- 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 @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 75c34d5c5ea0..84b5b4a0f157 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -1,4 +1,28 @@ + diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index d9634480ecec..7298b8a0b2f6 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -1,4 +1,28 @@ + diff --git a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java index de2fb95b0c18..e777c7c2b970 100644 --- a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.boot.SpringApplication; 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 index 1af8832eea1b..ec43618faec2 100644 --- 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 @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/price-microservice/src/main/resources/application.properties b/api-gateway/price-microservice/src/main/resources/application.properties index 49bd7ebf9ea0..f0b479dfdf0d 100644 --- a/api-gateway/price-microservice/src/main/resources/application.properties +++ b/api-gateway/price-microservice/src/main/resources/application.properties @@ -1 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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 index 230536fea577..c289549f251e 100644 --- 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 @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java index b475dfdb28f0..60f0b6d543a4 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java index 29829c8d51fe..9a644a0f7545 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java index ba43f0997b83..0a04db4022b9 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java index fd1ae635b3d6..935dab7781a7 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java index 62d426e8dd1a..64fde1e66230 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java index ed986f774770..db358b781fbd 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java index 2df566bb26f3..44497b99710c 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java index 67d984c315a8..bdc7cb00bd73 100644 --- a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java +++ b/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; diff --git a/api-gateway/src/main/resources/application.properties b/api-gateway/src/main/resources/application.properties index 81efb206b93a..69f58171237a 100644 --- a/api-gateway/src/main/resources/application.properties +++ b/api-gateway/src/main/resources/application.properties @@ -1 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java index f434f88570f3..d1a210f05db4 100644 --- a/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java +++ b/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.junit.Before; From a6036e578791c9d9f7652e9ee0193936a82543fb Mon Sep 17 00:00:00 2001 From: tmcconville Date: Tue, 3 May 2016 16:29:20 -0500 Subject: [PATCH 073/207] #297 Update category and tags in README --- api-gateway/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api-gateway/README.md b/api-gateway/README.md index 963f34761bb2..23014ae0b8af 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -3,10 +3,11 @@ layout: pattern title: API Gateway folder: api-gateway permalink: /patterns/api-gateway/ -categories: Other +categories: Architectural tags: - Java -- Difficulty-Beginner +- Difficulty-Intermediate +- Spring --- ## Intent From 8653eb87bca0e4d09c665bb1e1334b5d9434db46 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Tue, 3 May 2016 17:17:36 -0500 Subject: [PATCH 074/207] #297 Move API Gateway code into a new submodule: api-gateway-service. Change api-gateway packaging type to "pom". Update price-microservice and image-microservice to be true submodules of api-gateway --- api-gateway/api-gateway-service/pom.xml | 98 +++++++++++++++++++ .../com/iluwatar/api/gateway/ApiGateway.java | 0 .../java/com/iluwatar/api/gateway/App.java | 0 .../iluwatar/api/gateway/DesktopProduct.java | 0 .../com/iluwatar/api/gateway/ImageClient.java | 0 .../iluwatar/api/gateway/ImageClientImpl.java | 0 .../iluwatar/api/gateway/MobileProduct.java | 0 .../com/iluwatar/api/gateway/PriceClient.java | 0 .../iluwatar/api/gateway/PriceClientImpl.java | 0 .../src/main/resources/application.properties | 0 .../iluwatar/api/gateway/ApiGatewayTest.java | 0 api-gateway/image-microservice/pom.xml | 3 +- api-gateway/pom.xml | 67 ++----------- api-gateway/price-microservice/pom.xml | 3 +- 14 files changed, 106 insertions(+), 65 deletions(-) create mode 100644 api-gateway/api-gateway-service/pom.xml rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/ApiGateway.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/App.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/ImageClient.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/MobileProduct.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/PriceClient.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java (100%) rename api-gateway/{ => api-gateway-service}/src/main/resources/application.properties (100%) rename api-gateway/{ => api-gateway-service}/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java (100%) diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml new file mode 100644 index 000000000000..61b6ce5a39fe --- /dev/null +++ b/api-gateway/api-gateway-service/pom.xml @@ -0,0 +1,98 @@ + + + + + api-gateway + com.iluwatar + 1.12.0-SNAPSHOT + + 4.0.0 + api-gateway-service + jar + + + 4.2.5.RELEASE + 1.3.3.RELEASE + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/ApiGateway.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/App.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClient.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/MobileProduct.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClient.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java diff --git a/api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java similarity index 100% rename from api-gateway/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java rename to api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java diff --git a/api-gateway/src/main/resources/application.properties b/api-gateway/api-gateway-service/src/main/resources/application.properties similarity index 100% rename from api-gateway/src/main/resources/application.properties rename to api-gateway/api-gateway-service/src/main/resources/application.properties diff --git a/api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java similarity index 100% rename from api-gateway/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java rename to api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index 63fe074121e2..590aecf81ddd 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -27,8 +27,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - ../../pom.xml - java-design-patterns + api-gateway com.iluwatar 1.12.0-SNAPSHOT diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 84b5b4a0f157..73b2326bdbd9 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -33,66 +33,11 @@ 4.0.0 api-gateway - jar + pom - - 4.2.5.RELEASE - 1.3.3.RELEASE - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - junit - junit - test - - - org.mockito - mockito-core - test - - - org.apache.httpcomponents - httpclient - 4.5.2 - - - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot.version} - - - - repackage - - - - - - + + image-microservice + price-microservice + api-gateway-service + \ No newline at end of file diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index 7298b8a0b2f6..86874cb33a82 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -27,8 +27,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - ../../pom.xml - java-design-patterns + api-gateway com.iluwatar 1.12.0-SNAPSHOT From dd2385312b979583db0b1bfad31859f8367a32b5 Mon Sep 17 00:00:00 2001 From: tmcconville Date: Tue, 3 May 2016 17:37:33 -0500 Subject: [PATCH 075/207] #297 Add new dependencies to java-design-patterns/pom.xml and reference them from api-gateway submodule pom.xml files --- api-gateway/api-gateway-service/pom.xml | 11 ----------- api-gateway/image-microservice/pom.xml | 10 ---------- api-gateway/price-microservice/pom.xml | 10 ---------- pom.xml | 24 ++++++++++++++++++++++++ 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index 61b6ce5a39fe..4e4714c56877 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -35,19 +35,11 @@ api-gateway-service jar - - 4.2.5.RELEASE - 1.3.3.RELEASE - - org.springframework.boot spring-boot-dependencies - ${spring-boot.version} - pom - import @@ -55,12 +47,10 @@ org.springframework spring-webmvc - ${spring.version} org.springframework.boot spring-boot-starter-web - ${spring-boot.version} junit @@ -75,7 +65,6 @@ org.apache.httpcomponents httpclient - 4.5.2 diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index 590aecf81ddd..b5f2f504c1a3 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -36,19 +36,11 @@ image-microservice jar - - 4.2.5.RELEASE - 1.3.3.RELEASE - - org.springframework.boot spring-boot-dependencies - ${spring-boot.version} - pom - import @@ -56,12 +48,10 @@ org.springframework spring-webmvc - ${spring.version} org.springframework.boot spring-boot-starter-web - ${spring-boot.version} junit diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index 86874cb33a82..a39fde8a61f8 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -36,19 +36,11 @@ price-microservice jar - - 4.2.5.RELEASE - 1.3.3.RELEASE - - org.springframework.boot spring-boot-dependencies - ${spring-boot.version} - pom - import @@ -56,12 +48,10 @@ org.springframework spring-webmvc - ${spring.version} org.springframework.boot spring-boot-starter-web - ${spring-boot.version} junit diff --git a/pom.xml b/pom.xml index 800e91f4cf29..176ea9ee6696 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ UTF-8 5.0.1.Final 4.2.4.RELEASE + 1.3.3.RELEASE 1.9.2.RELEASE 1.4.190 4.12 @@ -46,6 +47,7 @@ 1.15.1 1.10.19 4.12.1 + 4.5.2 abstract-factory @@ -143,11 +145,33 @@ spring-test ${spring.version} + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + org.springframework.data spring-data-jpa ${spring-data.version} + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.apache.httpcomponents + httpclient + ${apache-httpcomponents.version} + com.h2database h2 From f234a6875cec63b0dd1d17e4035fb448030f4f7d Mon Sep 17 00:00:00 2001 From: tmcconville Date: Tue, 3 May 2016 17:41:37 -0500 Subject: [PATCH 076/207] #297 Update class diagram to reflect new location of files --- api-gateway/etc/api-gateway.ucls | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api-gateway/etc/api-gateway.ucls b/api-gateway/etc/api-gateway.ucls index 0a9fe4b029bc..4a74c2108b22 100644 --- a/api-gateway/etc/api-gateway.ucls +++ b/api-gateway/etc/api-gateway.ucls @@ -2,7 +2,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java" binary="false" corner="BOTTOM_RIGHT"> @@ -11,7 +11,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java" binary="false" corner="BOTTOM_RIGHT"> @@ -20,7 +20,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java" binary="false" corner="BOTTOM_RIGHT"> @@ -29,7 +29,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java" binary="false" corner="BOTTOM_RIGHT"> @@ -38,7 +38,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java" binary="false" corner="BOTTOM_RIGHT"> @@ -47,7 +47,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java" binary="false" corner="BOTTOM_RIGHT"> @@ -56,7 +56,7 @@ + file="/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java" binary="false" corner="BOTTOM_RIGHT"> From 2ed3748c9bf0a6170cb07e647d7d758b4f6d6114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 7 May 2016 11:38:55 +0300 Subject: [PATCH 077/207] Improve Singleton holder example --- .../InitializingOnDemandHolderIdiom.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 84444ec2ee03..4aa6afe1203e 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -22,35 +22,38 @@ */ package com.iluwatar.singleton; -import java.io.Serializable; - /** - * The Initialize-on-demand-holder idiom is a secure way of creating lazy initialized singleton - * object in Java. refer to "The CERT Oracle Secure Coding Standard for Java" By Dhruv Mohindra, - * Robert C. Seacord p.378 - *

- * Singleton objects usually are heavy to create and sometimes need to serialize them. This class - * also shows how to preserve singleton in serialized version of singleton. + * The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton + * object in Java. + *

+ * The technique is is as lazy as possible and works in all known versions of Java. It takes advantage + * of language guarantees about class initialization, and will therefore work correctly in all + * Java-compliant compilers and virtual machines. + *

+ * The inner class is referenced no earlier (and therefore loaded no earlier by the class loader) than + * the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special + * language constructs (i.e. volatile or synchronized). * - * @author mortezaadi@gmail.com */ -public final class InitializingOnDemandHolderIdiom implements Serializable { - - private static final long serialVersionUID = 1L; +public final class InitializingOnDemandHolderIdiom { + /** + * Private constructor. + */ private InitializingOnDemandHolderIdiom() {} + /** + * @return Singleton instance + */ public static InitializingOnDemandHolderIdiom getInstance() { return HelperHolder.INSTANCE; } - protected Object readResolve() { - return getInstance(); - } - + /** + * Provides the lazy-loaded Singleton instance. + */ private static class HelperHolder { public static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); } - } From 75ed1b1a9684eb2a2cad4ae36dcbb066069a908a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 21 May 2016 21:48:25 +0300 Subject: [PATCH 078/207] Review fixes --- hexagonal/pom.xml | 4 ---- .../administration/LotteryAdministration.java | 11 ++++++++++ .../LotteryAdministrationImpl.java | 4 ---- .../hexagonal/banking/WireTransfers.java | 11 ++++++++++ .../database/LotteryTicketRepository.java | 15 +++++++++++++ .../domain/LotteryTicketCheckResult.java | 1 - .../hexagonal/domain/LotteryTicketId.java | 3 +++ .../notifications/LotteryNotifications.java | 21 ++++++++++++++++++- .../hexagonal/service/LotteryService.java | 6 ++++++ .../hexagonal/service/LotteryServiceImpl.java | 5 ++++- 10 files changed, 70 insertions(+), 11 deletions(-) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 540e6f398367..54f81cf144e5 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -32,11 +32,7 @@ java-design-patterns 1.12.0-SNAPSHOT - com.iluwatar.hexagonal hexagonal - 1.0-SNAPSHOT - hexagonal - http://maven.apache.org junit diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index 70b625aa2ece..bc625b2301cc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -35,8 +35,19 @@ */ public interface LotteryAdministration { + /** + * Get all the lottery tickets submitted for lottery + */ Map getAllSubmittedTickets(); + + /** + * Draw lottery numbers + */ LotteryNumbers performLottery(); + + /** + * Begin new lottery round + */ void resetLottery(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 5e457439bd5a..a452600aa1de 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -47,13 +47,9 @@ public class LotteryAdministrationImpl implements LotteryAdministration { private final LotteryTicketRepository repository; - private final LotteryService service = new LotteryServiceImpl(); - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - private final WireTransfers bank = new WireTransfersImpl(); - public LotteryAdministrationImpl() { repository = new LotteryTicketInMemoryRepository(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java index 61a6567bae6b..7d21e7bf328f 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java @@ -29,8 +29,19 @@ */ public interface WireTransfers { + /** + * Set amount of funds for bank account + */ void setFunds(String bankAccount, int amount); + + /** + * Get amount of funds for bank account + */ int getFunds(String bankAccount); + + /** + * Transfer funds from one bank account to another + */ boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java index a531440b4eeb..4c6522838a75 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java @@ -35,9 +35,24 @@ */ public interface LotteryTicketRepository { + /** + * Find lottery ticket by id + */ Optional findById(LotteryTicketId id); + + /** + * Save lottery ticket + */ Optional save(LotteryTicket ticket); + + /** + * Get all lottery tickets + */ Map findAll(); + + /** + * Delete all lottery tickets + */ void deleteAll(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java index 534dc685f80a..ded65d27032c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -32,7 +32,6 @@ public class LotteryTicketCheckResult { public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED }; private final CheckResult checkResult; - private final int prizeAmount; /** diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java index 358bdd05a14c..710091222b2d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -24,6 +24,9 @@ import java.util.UUID; +/** + * Lottery ticked id + */ public class LotteryTicketId { private final UUID id; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java index cc9f9d6e5bd2..d7a0cc870348 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -30,11 +30,30 @@ * */ public interface LotteryNotifications { - + + /** + * Notify lottery ticket was submitted + */ void notifyTicketSubmitted(PlayerDetails details); + + /** + * Notify there was an error submitting lottery ticket + */ void notifyTicketSubmitError(PlayerDetails details); + + /** + * Notify lottery ticket did not win + */ void notifyNoWin(PlayerDetails details); + + /** + * Notify that prize has been paid + */ void notifyPrize(PlayerDetails details, int prizeAmount); + + /** + * Notify that there was an error paying the prize + */ void notifyPrizeError(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index 65270410aa68..0056e794b0d5 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -36,7 +36,13 @@ */ public interface LotteryService { + /** + * Submit lottery ticket to participate in the lottery + */ Optional submitTicket(LotteryTicket ticket); + /** + * Check if lottery ticket has won + */ LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index d446e93be025..58df1c7c8d39 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -49,7 +49,10 @@ public class LotteryServiceImpl implements LotteryService { private final WireTransfers bank = new WireTransfersImpl(); private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - + + /** + * Constructor + */ public LotteryServiceImpl() { repository = new LotteryTicketInMemoryRepository(); } From f6649a4fed783f7278bbfe2102a757a72cd5e38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 22 May 2016 09:33:50 +0300 Subject: [PATCH 079/207] Markdown fix --- hexagonal/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hexagonal/README.md b/hexagonal/README.md index 5e954e90c819..b1d0a7948ee2 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -10,9 +10,9 @@ tags: --- ## Also known as -Ports and Adapters -Clean Architecture -Onion Architecture +* Ports and Adapters +* Clean Architecture +* Onion Architecture ## Intent Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. From 509de9782dacfa927505215ff08cb8fa52eac223 Mon Sep 17 00:00:00 2001 From: VarunVats9 Date: Sun, 29 May 2016 20:05:54 +0530 Subject: [PATCH 080/207] renamed HeroBuilder to Builder --- builder/etc/builder.ucls | 2 +- .../src/main/java/com/iluwatar/builder/App.java | 12 ++++++------ .../src/main/java/com/iluwatar/builder/Hero.java | 14 +++++++------- .../test/java/com/iluwatar/builder/HeroTest.java | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/builder/etc/builder.ucls b/builder/etc/builder.ucls index 06a83ced7f79..073c5cea70f6 100644 --- a/builder/etc/builder.ucls +++ b/builder/etc/builder.ucls @@ -28,7 +28,7 @@ - * We want to build {@link Hero} objects, but its construction is complex because of the many - * parameters needed. To aid the user we introduce {@link HeroBuilder} class. {@link HeroBuilder} + * parameters needed. To aid the user we introduce {@link Builder} class. {@link Hero.Builder} * takes the minimum parameters to build {@link Hero} object in its constructor. After that * additional configuration for the {@link Hero} object can be done using the fluent - * {@link HeroBuilder} interface. When configuration is ready the build method is called to receive + * {@link Builder} interface. When configuration is ready the build method is called to receive * the final {@link Hero} object. * */ @@ -58,18 +58,18 @@ public class App { public static void main(String[] args) { Hero mage = - new HeroBuilder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) + new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) .withWeapon(Weapon.DAGGER).build(); System.out.println(mage); Hero warrior = - new HeroBuilder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) + new Hero.Builder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) .build(); System.out.println(warrior); Hero thief = - new HeroBuilder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) + new Hero.Builder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) .withWeapon(Weapon.BOW).build(); System.out.println(thief); diff --git a/builder/src/main/java/com/iluwatar/builder/Hero.java b/builder/src/main/java/com/iluwatar/builder/Hero.java index 1835df6384b5..25445851a2bb 100644 --- a/builder/src/main/java/com/iluwatar/builder/Hero.java +++ b/builder/src/main/java/com/iluwatar/builder/Hero.java @@ -36,7 +36,7 @@ public final class Hero { private final Armor armor; private final Weapon weapon; - private Hero(HeroBuilder builder) { + private Hero(Builder builder) { this.profession = builder.profession; this.name = builder.name; this.hairColor = builder.hairColor; @@ -102,7 +102,7 @@ public String toString() { * The builder class. * */ - public static class HeroBuilder { + public static class Builder { private final Profession profession; private final String name; @@ -114,7 +114,7 @@ public static class HeroBuilder { /** * Constructor */ - public HeroBuilder(Profession profession, String name) { + public Builder(Profession profession, String name) { if (profession == null || name == null) { throw new IllegalArgumentException("profession and name can not be null"); } @@ -122,22 +122,22 @@ public HeroBuilder(Profession profession, String name) { this.name = name; } - public HeroBuilder withHairType(HairType hairType) { + public Builder withHairType(HairType hairType) { this.hairType = hairType; return this; } - public HeroBuilder withHairColor(HairColor hairColor) { + public Builder withHairColor(HairColor hairColor) { this.hairColor = hairColor; return this; } - public HeroBuilder withArmor(Armor armor) { + public Builder withArmor(Armor armor) { this.armor = armor; return this; } - public HeroBuilder withWeapon(Weapon weapon) { + public Builder withWeapon(Weapon weapon) { this.weapon = weapon; return this; } diff --git a/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java index 3c308e8c1e05..b0a73a9d0ed7 100644 --- a/builder/src/test/java/com/iluwatar/builder/HeroTest.java +++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java @@ -39,7 +39,7 @@ public class HeroTest { */ @Test(expected = IllegalArgumentException.class) public void testMissingProfession() throws Exception { - new Hero.HeroBuilder(null, "Sir without a job"); + new Hero.Builder(null, "Sir without a job"); } /** @@ -47,7 +47,7 @@ public void testMissingProfession() throws Exception { */ @Test(expected = IllegalArgumentException.class) public void testMissingName() throws Exception { - new Hero.HeroBuilder(Profession.THIEF, null); + new Hero.Builder(Profession.THIEF, null); } /** @@ -57,7 +57,7 @@ public void testMissingName() throws Exception { public void testBuildHero() throws Exception { final String heroName = "Sir Lancelot"; - final Hero hero = new Hero.HeroBuilder(Profession.WARRIOR, heroName) + final Hero hero = new Hero.Builder(Profession.WARRIOR, heroName) .withArmor(Armor.CHAIN_MAIL) .withWeapon(Weapon.SWORD) .withHairType(HairType.LONG_CURLY) From c1187ae0d787b39ca4019b2a9d5a16d031bef08f Mon Sep 17 00:00:00 2001 From: qza Date: Tue, 31 May 2016 20:12:27 +0200 Subject: [PATCH 081/207] #355 init abstract-document module --- abstract-document/README.md | 26 +++++++++++++++++++++++ abstract-document/pom.xml | 42 +++++++++++++++++++++++++++++++++++++ pom.xml | 3 ++- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 abstract-document/README.md create mode 100644 abstract-document/pom.xml diff --git a/abstract-document/README.md b/abstract-document/README.md new file mode 100644 index 000000000000..d9538c0bd7a5 --- /dev/null +++ b/abstract-document/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Abstract Document +folder: abstract-document +permalink: /patterns/abstract-document/ +categories: Structural +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Achieve flexibility of untyped languages and keep the type-safety + +![alt text](./etc/abstract-document_1.png "Abstract Document") + +## Applicability +Use the Abstract Document Pattern when + +* there is a need for dynamic properties +* you want a better way to organize domain +* you want loosely coupled system with flexibility of untyped languages + +## Real world examples + +* [Speedment](https://github.com/speedment/speedment) \ No newline at end of file diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml new file mode 100644 index 000000000000..e7fc58047a97 --- /dev/null +++ b/abstract-document/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.12.0-SNAPSHOT + + abstract-document + + + junit + junit + test + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index af807b8baafd..362519090b36 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,8 @@ mutex semaphore hexagonal - + abstract-document + From 913411773f708a5d3d73cc7953a75c596f9bd7cd Mon Sep 17 00:00:00 2001 From: qza Date: Wed, 1 Jun 2016 22:20:55 +0200 Subject: [PATCH 082/207] #355 document, abstract base, traits and example domain --- .../abstractdocument/AbstractDocument.java | 38 +++++++++++++++++++ .../iluwatar/abstractdocument/Document.java | 34 +++++++++++++++++ .../iluwatar/abstractdocument/domain/Car.java | 18 +++++++++ .../abstractdocument/domain/HasModel.java | 13 +++++++ .../abstractdocument/domain/HasParts.java | 13 +++++++ .../abstractdocument/domain/HasPrice.java | 13 +++++++ .../abstractdocument/domain/Part.java | 18 +++++++++ 7 files changed, 147 insertions(+) create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java new file mode 100644 index 000000000000..d4aa8ed5b2d9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -0,0 +1,38 @@ +package com.iluwatar.abstractdocument; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; + +public abstract class AbstractDocument implements Document { + + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public Stream children(String key, Function, T> constructor) { + return Stream.of(get(key)) + .filter(el -> el != null) + .map(el -> (List>) el) + .findFirst().get().stream() + .map(constructor); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java new file mode 100644 index 000000000000..8d4db30b9624 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -0,0 +1,34 @@ +package com.iluwatar.abstractdocument; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +public interface Document { + + /** + * Puts the value related to the key + * + * @param key + * @param value + * @return Void + */ + Void put(String key, Object value); + + /** + * Gets the value for the key + * + * @param key + * @return value or null + */ + Object get(String key); + + /** + * Gets the stream of child documents + * + * @param key + * @param constructor + * @return child documents + */ + Stream children(String key, Function, T> constructor); +} 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 new file mode 100644 index 000000000000..70e31e70e09d --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -0,0 +1,18 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.HashMap; +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { + + protected Car() { + super(new HashMap()); + } + + protected 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 new file mode 100644 index 000000000000..635f80abf6fd --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java @@ -0,0 +1,13 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +public interface HasModel extends Document { + + default Optional getModel() { + return Optional.ofNullable((String) get("model")); + } + +} 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 new file mode 100644 index 000000000000..92c7dc7d9cc6 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java @@ -0,0 +1,13 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.stream.Stream; + +import com.iluwatar.abstractdocument.Document; + +public interface HasParts extends Document { + + default Stream getParts() { + return children("parts", 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 new file mode 100644 index 000000000000..7c9e8e6a17ee --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java @@ -0,0 +1,13 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +public interface HasPrice extends Document { + + default Optional getPartner() { + return Optional.ofNullable((Number) get("price")); + } + +} 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 new file mode 100644 index 000000000000..fa252a7bcf7b --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -0,0 +1,18 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.HashMap; +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +public class Part extends AbstractDocument implements HasModel, HasPrice { + + protected Part() { + super(new HashMap()); + } + + protected Part(Map properties) { + super(properties); + } + +} From 32b647323c845733fd7b8bfc93752a376b0543ae Mon Sep 17 00:00:00 2001 From: qza Date: Wed, 1 Jun 2016 22:21:31 +0200 Subject: [PATCH 083/207] #355 class diagrams --- .../etc/abstract-document-base.png | Bin 0 -> 14973 bytes .../etc/abstract-document-base.ucls | 58 +++++++++++ abstract-document/etc/abstract-document.png | Bin 0 -> 16712 bytes abstract-document/etc/abstract-document.ucls | 96 ++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 abstract-document/etc/abstract-document-base.png create mode 100644 abstract-document/etc/abstract-document-base.ucls create mode 100644 abstract-document/etc/abstract-document.png create mode 100644 abstract-document/etc/abstract-document.ucls diff --git a/abstract-document/etc/abstract-document-base.png b/abstract-document/etc/abstract-document-base.png new file mode 100644 index 0000000000000000000000000000000000000000..13345dbb862c12463c352af53dafb7f3ee42bf37 GIT binary patch literal 14973 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 T^}xTC-jjMQFIFP*#_#_C2aR!W literal 0 HcmV?d00001 diff --git a/abstract-document/etc/abstract-document-base.ucls b/abstract-document/etc/abstract-document-base.ucls new file mode 100644 index 000000000000..bfe927ed9357 --- /dev/null +++ b/abstract-document/etc/abstract-document-base.ucls @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstract-document/etc/abstract-document.png b/abstract-document/etc/abstract-document.png new file mode 100644 index 0000000000000000000000000000000000000000..98d186f7e7aa8de23be6656c58332c826db77aa7 GIT binary patch literal 16712 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From a372f05e41910ed86b5d9d2d6cfd43ad1f964a24 Mon Sep 17 00:00:00 2001 From: qza Date: Thu, 2 Jun 2016 07:27:35 +0200 Subject: [PATCH 084/207] #355 override toString to log properties --- .../abstractdocument/AbstractDocument.java | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) 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 d4aa8ed5b2d9..c3f8e2a4b7bf 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -8,31 +8,42 @@ public abstract class AbstractDocument implements Document { - private final Map properties; - - protected AbstractDocument(Map properties) { - Objects.requireNonNull(properties, "properties map is required"); - this.properties = properties; - } - - @Override - public Void put(String key, Object value) { - properties.put(key, value); - return null; - } - - @Override - public Object get(String key) { - return properties.get(key); - } - - @Override - public Stream children(String key, Function, T> constructor) { - return Stream.of(get(key)) - .filter(el -> el != null) - .map(el -> (List>) el) - .findFirst().get().stream() - .map(constructor); - } + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public Stream children(String key, Function, T> constructor) { + return Stream.of(get(key)) + .filter(el -> el != null) + .map(el -> (List>) el) + .findAny().get().stream() + .map(constructor); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append("["); + properties.entrySet().forEach(e -> + builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]") + ); + builder.append("]"); + return builder.toString(); + } } From 43f90ead48709c44d5253864d3d31c6bc24b0601 Mon Sep 17 00:00:00 2001 From: qza Date: Thu, 2 Jun 2016 07:29:37 +0200 Subject: [PATCH 085/207] #355 abstract document test --- .../AbstractDocumentTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java new file mode 100644 index 000000000000..43585d853614 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -0,0 +1,48 @@ +package com.iluwatar.abstractdocument; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +public class AbstractDocumentTest { + + private static final String KEY = "key"; + private static final Object VALUE = "value"; + + private class DocumentImplementation extends AbstractDocument { + + DocumentImplementation(Map properties) { + super(properties); + } + } + + private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); + + @Test + public void shouldPutAndGetValue() { + document.put(KEY, VALUE); + assertEquals(VALUE, document.get(KEY)); + System.out.println(document); + } + + @Test + public void shouldRetrieveChildren() { + Map child1 = new HashMap<>(); + Map child2 = new HashMap<>(); + List> children = Arrays.asList(child1, child2); + + document.put(KEY, children); + + Stream childrenStream = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(2, childrenStream.count()); + } + +} From f3110de1306659b6cd70a318a1070a28fefc680e Mon Sep 17 00:00:00 2001 From: qza Date: Thu, 2 Jun 2016 07:32:48 +0200 Subject: [PATCH 086/207] #355 handle case when there are no child elements for the given key --- .../com/iluwatar/abstractdocument/AbstractDocument.java | 7 ++++--- .../iluwatar/abstractdocument/AbstractDocumentTest.java | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) 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 c3f8e2a4b7bf..f5ef69e3ccaa 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -3,6 +3,7 @@ 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; @@ -28,11 +29,11 @@ public Object get(String key) { @Override public Stream children(String key, Function, T> constructor) { - return Stream.of(get(key)) + Optional>> any = Stream.of(get(key)) .filter(el -> el != null) .map(el -> (List>) el) - .findAny().get().stream() - .map(constructor); + .findAny(); + return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty(); } @Override 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 43585d853614..baf872e4f19b 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -29,7 +29,6 @@ private class DocumentImplementation extends AbstractDocument { public void shouldPutAndGetValue() { document.put(KEY, VALUE); assertEquals(VALUE, document.get(KEY)); - System.out.println(document); } @Test @@ -45,4 +44,11 @@ public void shouldRetrieveChildren() { assertEquals(2, childrenStream.count()); } + @Test + public void shouldRetrieveEmptyStreamForNonExistinChildren() { + Stream children = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(0, children.count()); + } + } From c229ec23b3f9d40a5f91f6865e299307a6117727 Mon Sep 17 00:00:00 2001 From: qza Date: Thu, 2 Jun 2016 07:41:32 +0200 Subject: [PATCH 087/207] #355 clean up --- .../java/com/iluwatar/abstractdocument/AbstractDocument.java | 2 +- .../src/main/java/com/iluwatar/abstractdocument/domain/Car.java | 2 +- .../main/java/com/iluwatar/abstractdocument/domain/Part.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 f5ef69e3ccaa..054b6ebfbdec 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -38,7 +38,7 @@ public Stream children(String key, Function, T> const @Override public String toString() { - final StringBuilder builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(); builder.append(getClass().getName()).append("["); properties.entrySet().forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]") 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 70e31e70e09d..b3dcaa77f3b4 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 @@ -8,7 +8,7 @@ public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { protected Car() { - super(new HashMap()); + super(new HashMap<>()); } protected Car(Map properties) { 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 fa252a7bcf7b..c6e1a3908448 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 @@ -8,7 +8,7 @@ public class Part extends AbstractDocument implements HasModel, HasPrice { protected Part() { - super(new HashMap()); + super(new HashMap<>()); } protected Part(Map properties) { From afdeba4f9abbc18592585f98c0809517b7f80f33 Mon Sep 17 00:00:00 2001 From: qza Date: Sat, 4 Jun 2016 20:06:32 +0200 Subject: [PATCH 088/207] #355 finalize example --- abstract-document/README.md | 17 ++-- .../abstractdocument/AbstractDocument.java | 96 ++++++++++++------- .../com/iluwatar/abstractdocument/App.java | 88 +++++++++++++++++ .../iluwatar/abstractdocument/Document.java | 71 +++++++++----- .../iluwatar/abstractdocument/domain/Car.java | 36 +++++-- .../abstractdocument/domain/HasModel.java | 35 ++++++- .../abstractdocument/domain/HasParts.java | 35 ++++++- .../abstractdocument/domain/HasPrice.java | 35 ++++++- .../abstractdocument/domain/HasType.java | 40 ++++++++ .../abstractdocument/domain/Part.java | 38 ++++++-- .../AbstractDocumentTest.java | 90 +++++++++++------ .../iluwatar/abstractdocument/AppTest.java | 37 +++++++ .../iluwatar/abstractdocument/DomainTest.java | 77 +++++++++++++++ pom.xml | 2 +- 14 files changed, 573 insertions(+), 124 deletions(-) create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java create mode 100644 abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java create mode 100644 abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java diff --git a/abstract-document/README.md b/abstract-document/README.md index d9538c0bd7a5..bf28ff999bf5 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -12,15 +12,20 @@ tags: ## Intent Achieve flexibility of untyped languages and keep the type-safety -![alt text](./etc/abstract-document_1.png "Abstract Document") +![alt text](./etc/abstract-document-base.png "Abstract Document Base") + +![alt text](./etc/abstract-document.png "Abstract Document Traits and Domain") + ## Applicability Use the Abstract Document Pattern when -* there is a need for dynamic properties -* you want a better way to organize domain -* you want loosely coupled system with flexibility of untyped languages +* there is a need to add new properties on the fly +* you want a flexible way to organize domain in tree like structure +* you want more loosely coupled system + -## Real world examples +## Credits -* [Speedment](https://github.com/speedment/speedment) \ No newline at end of file +* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern) +* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) \ No newline at end of file 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 054b6ebfbdec..4bf8f0d146ec 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; import java.util.List; @@ -7,44 +29,44 @@ import java.util.function.Function; import java.util.stream.Stream; +/** + * Abstract implementation of Document interface + */ public abstract class AbstractDocument implements Document { - private final Map properties; - - protected AbstractDocument(Map properties) { - Objects.requireNonNull(properties, "properties map is required"); - this.properties = properties; - } - - @Override - public Void put(String key, Object value) { - properties.put(key, value); - return null; - } - - @Override - public Object get(String key) { - return properties.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(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(getClass().getName()).append("["); - properties.entrySet().forEach(e -> - builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]") - ); - builder.append("]"); - return builder.toString(); - } + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.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(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append("["); + properties.entrySet() + .forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).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 new file mode 100644 index 000000000000..d7758b6f745e --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +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 java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * 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. + */ +public class App { + + /** + * Executes the App + */ + public App() { + System.out.println("Constructing parts and car"); + + Map carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, "300SL"); + carProperties.put(HasPrice.PROPERTY, 10000L); + + Map wheelProperties = new HashMap<>(); + wheelProperties.put(HasType.PROPERTY, "wheel"); + wheelProperties.put(HasModel.PROPERTY, "15C"); + wheelProperties.put(HasPrice.PROPERTY, 100L); + + Map doorProperties = new HashMap<>(); + doorProperties.put(HasType.PROPERTY, "door"); + doorProperties.put(HasModel.PROPERTY, "Lambo"); + doorProperties.put(HasPrice.PROPERTY, 300L); + + carProperties.put(HasParts.PROPERTY, Arrays.asList(wheelProperties, doorProperties)); + + Car car = new Car(carProperties); + + System.out.println("Here is our car:"); + System.out.println("-> model: " + car.getModel().get()); + System.out.println("-> price: " + car.getPrice().get()); + System.out.println("-> parts: "); + car.getParts().forEach(p -> System.out + .println("\t" + p.getType().get() + "/" + p.getModel().get() + "/" + p.getPrice().get())); + } + + /** + * 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 8d4db30b9624..7705f37eb357 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -1,34 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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; import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; +/** + * Document interface + */ public interface Document { - /** - * Puts the value related to the key - * - * @param key - * @param value - * @return Void - */ - Void put(String key, Object value); + /** + * Puts the value related to the key + * + * @param key element key + * @param value element value + * @return Void + */ + Void put(String key, Object value); - /** - * Gets the value for the key - * - * @param key - * @return value or null - */ - Object get(String key); + /** + * Gets the value for the key + * + * @param key element key + * @return value or null + */ + Object get(String key); - /** - * Gets the stream of child documents - * - * @param key - * @param constructor - * @return child documents - */ - Stream children(String key, Function, T> constructor); + /** + * Gets the stream of child documents + * + * @param key element key + * @param constructor constructor of child class + * @return child documents + */ + Stream children(String key, Function, T> constructor); } 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 b3dcaa77f3b4..e29ee63da77d 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,18 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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; -import java.util.HashMap; import java.util.Map; import com.iluwatar.abstractdocument.AbstractDocument; +/** + * Car entity + */ public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { - protected Car() { - super(new HashMap<>()); - } - - protected Car(Map properties) { - super(properties); - } + 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 635f80abf6fd..fbd8c0d7fd5c 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,13 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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; import java.util.Optional; import com.iluwatar.abstractdocument.Document; +/** + * HasModel trait for static access to 'model' property + */ public interface HasModel extends Document { - - default Optional getModel() { - return Optional.ofNullable((String) get("model")); - } + + String PROPERTY = "model"; + + default Optional getModel() { + return Optional.ofNullable((String) get(PROPERTY)); + } } 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 92c7dc7d9cc6..581702cc9f1a 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,13 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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; import java.util.stream.Stream; import com.iluwatar.abstractdocument.Document; +/** + * HasParts trait for static access to 'parts' property + */ public interface HasParts extends Document { - - default Stream getParts() { - return children("parts", Part::new); - } + + String PROPERTY = "parts"; + + default Stream getParts() { + return children(PROPERTY, 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 7c9e8e6a17ee..3d1d0e3e7562 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,13 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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; import java.util.Optional; import com.iluwatar.abstractdocument.Document; +/** + * HasPrice trait for static access to 'price' property + */ public interface HasPrice extends Document { - - default Optional getPartner() { - return Optional.ofNullable((Number) get("price")); - } + + String PROPERTY = "price"; + + default Optional getPrice() { + return Optional.ofNullable((Number) get(PROPERTY)); + } } 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 new file mode 100644 index 000000000000..b0f292bb6ae5 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +import com.iluwatar.abstractdocument.Document; + +import java.util.Optional; + +/** + * 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)); + } + +} 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 c6e1a3908448..e42f099d9bc6 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,18 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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; -import java.util.HashMap; import java.util.Map; import com.iluwatar.abstractdocument.AbstractDocument; -public class Part extends AbstractDocument implements HasModel, HasPrice { +/** + * Part entity + */ +public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { - protected Part() { - super(new HashMap<>()); - } - - protected Part(Map properties) { - super(properties); - } + public Part(Map properties) { + super(properties); + } } 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 baf872e4f19b..b6467e232598 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; import org.junit.Test; @@ -11,44 +33,56 @@ import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; +/** + * AbstractDocument test class + */ public class AbstractDocumentTest { - private static final String KEY = "key"; - private static final Object VALUE = "value"; + private static final String KEY = "key"; + private static final String VALUE = "value"; - private class DocumentImplementation extends AbstractDocument { + private class DocumentImplementation extends AbstractDocument { - DocumentImplementation(Map properties) { - super(properties); - } + DocumentImplementation(Map properties) { + super(properties); } + } - private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); + private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); - @Test - public void shouldPutAndGetValue() { - document.put(KEY, VALUE); - assertEquals(VALUE, document.get(KEY)); - } + @Test + public 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); + @Test + public void shouldRetrieveChildren() { + Map child1 = new HashMap<>(); + Map child2 = new HashMap<>(); + List> children = Arrays.asList(child1, child2); - document.put(KEY, children); + document.put(KEY, children); - Stream childrenStream = document.children(KEY, DocumentImplementation::new); - assertNotNull(children); - assertEquals(2, childrenStream.count()); - } + Stream childrenStream = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(2, childrenStream.count()); + } - @Test - public void shouldRetrieveEmptyStreamForNonExistinChildren() { - Stream children = document.children(KEY, DocumentImplementation::new); - assertNotNull(children); - assertEquals(0, children.count()); - } + @Test + public void shouldRetrieveEmptyStreamForNonExistingChildren() { + Stream 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)); + } } diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java new file mode 100644 index 000000000000..787ae3aa6468 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +import org.junit.Test; + +/** + * Simple App test + */ +public class AppTest { + + @Test + public void shouldExecuteAppWithoutException() { + 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 new file mode 100644 index 000000000000..437244a3d34f --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +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 java.util.Map; + +import static junit.framework.TestCase.assertEquals; + +/** + * Test for Part and Car + */ +public class DomainTest { + + private static final String TEST_PART_TYPE = "test-part-type"; + private static final String TEST_PART_MODEL = "test-part-model"; + private static final long TEST_PART_PRICE = 0L; + + private static final String TEST_CAR_MODEL = "test-car-model"; + 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()); + } + + @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()); + assertEquals(2, car.getParts().count()); + } + +} diff --git a/pom.xml b/pom.xml index 362519090b36..0e3318fb6050 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ mute-idiom mutex semaphore - hexagonal + hexagonal abstract-document From 53fc8b749abd9411d896dad3431ef13b5d45ffa9 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sat, 18 Jun 2016 15:40:03 +0100 Subject: [PATCH 089/207] Page Object pattern implementation --- page-object/pom.xml | 40 ++++ page-object/sample-application/pom.xml | 35 ++++ .../java/com/iluwatar/pageobject/App.java | 93 +++++++++ .../src/main/resources/credentials.txt | 25 +++ .../main/resources/sample-ui/album-list.html | 60 ++++++ .../main/resources/sample-ui/album-page.html | 74 +++++++ .../resources/sample-ui/css/album-list.css | 22 +++ .../main/resources/sample-ui/css/style.css | 3 + .../src/main/resources/sample-ui/login.html | 48 +++++ page-object/test-automation/pom.xml | 48 +++++ .../src/main/java/com/iluwatar/Page.java | 57 ++++++ .../com/iluwatar/pages/AlbumListPage.java | 95 +++++++++ .../java/com/iluwatar/pages/AlbumPage.java | 185 ++++++++++++++++++ .../java/com/iluwatar/pages/LoginPage.java | 120 ++++++++++++ .../pageobject/AlbumListPageTest.java | 45 +++++ .../iluwatar/pageobject/AlbumPageTest.java | 56 ++++++ .../iluwatar/pageobject/LoginPageTest.java | 45 +++++ pom.xml | 3 +- 18 files changed, 1053 insertions(+), 1 deletion(-) create mode 100644 page-object/pom.xml create mode 100644 page-object/sample-application/pom.xml create mode 100644 page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java create mode 100644 page-object/sample-application/src/main/resources/credentials.txt create mode 100644 page-object/sample-application/src/main/resources/sample-ui/album-list.html create mode 100644 page-object/sample-application/src/main/resources/sample-ui/album-page.html create mode 100644 page-object/sample-application/src/main/resources/sample-ui/css/album-list.css create mode 100644 page-object/sample-application/src/main/resources/sample-ui/css/style.css create mode 100644 page-object/sample-application/src/main/resources/sample-ui/login.html create mode 100644 page-object/test-automation/pom.xml create mode 100644 page-object/test-automation/src/main/java/com/iluwatar/Page.java create mode 100644 page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java create mode 100644 page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java create mode 100644 page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java create mode 100644 page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java create mode 100644 page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java create mode 100644 page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java diff --git a/page-object/pom.xml b/page-object/pom.xml new file mode 100644 index 000000000000..00f87ac9d8b6 --- /dev/null +++ b/page-object/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.12.0-SNAPSHOT + + page-object + pom + + sample-application + test-automation + + diff --git a/page-object/sample-application/pom.xml b/page-object/sample-application/pom.xml new file mode 100644 index 000000000000..74116fdb3408 --- /dev/null +++ b/page-object/sample-application/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + page-object + com.iluwatar + 1.12.0-SNAPSHOT + + sample-application + diff --git a/page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java b/page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java new file mode 100644 index 000000000000..155097c73c8f --- /dev/null +++ b/page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java @@ -0,0 +1,93 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; + +/** + * Page Object pattern wraps an UI component with an application specific API allowing you to + * manipulate the UI elements without having to dig around with the underlying UI technology used. This is + * especially useful for testing as it means your tests will be less brittle. Your tests can concentrate on + * the actual test cases where as the manipulation of the UI can be left to the internals of the page object + * itself. + * + *

+ * Due to this reason, it has become very popular within the test automation community. + * In particular, it is very common in that the page object is used to represent the html pages of a + * web application that is under test. This web application is referred to as AUT (Application Under Test). + * A web browser automation tool/framework like Selenium for instance, is then used to drive the automating + * of the browser navigation and user actions journeys through this web application. Your test class would + * therefore only be responsible for particular test cases and page object would be used by the test class + * for UI manipulation required for the tests. + * + *

+ * In this implementation rather than using Selenium, the HtmlUnit library is used as a replacement to + * represent the specific html elements and to drive the browser. The purpose of this example is just to + * provide a simple version that showcase the intentions of this pattern and how this pattern is used + * in order to understand it. + */ +public final class App { + + private App() { + } + + /** + * Application entry point + * + *

+ * The application under development is a web application. Normally you would probably have a + * backend that is probably implemented in an object-oriented language (e.g. Java) that serves + * the frontend which comprises of a series of HTML, CSS, JS etc... + * + *

+ * For illustrations purposes only, a very simple static html app is used here. This main method + * just fires up this simple web app in a default browser. + * + * @param args arguments + */ + public static void main(String[] args) { + + String currentWorkingDir = System.getProperty("user.dir"); + File applicationFile = new File(currentWorkingDir + + "/page-object/sample-application/src/main/resources/sample-ui/login.html"); + + // should work for unix like OS (mac, unix etc...) + if (Desktop.isDesktopSupported()) { + try { + Desktop.getDesktop().open(applicationFile); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + // java Desktop not supported - above unlikely to work for Windows so try following instead... + try { + Runtime.getRuntime().exec("cmd.exe start " + applicationFile); + } catch (IOException e) { + e.printStackTrace(); + } + + } + } +} diff --git a/page-object/sample-application/src/main/resources/credentials.txt b/page-object/sample-application/src/main/resources/credentials.txt new file mode 100644 index 000000000000..6f1bf2fb6b35 --- /dev/null +++ b/page-object/sample-application/src/main/resources/credentials.txt @@ -0,0 +1,25 @@ +==== + The MIT License + Copyright (c) 2014 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. +==== + +username - admin +password - password \ No newline at end of file diff --git a/page-object/sample-application/src/main/resources/sample-ui/album-list.html b/page-object/sample-application/src/main/resources/sample-ui/album-list.html new file mode 100644 index 000000000000..e02e65cf3b5a --- /dev/null +++ b/page-object/sample-application/src/main/resources/sample-ui/album-list.html @@ -0,0 +1,60 @@ + + + + + + Album List + + + + +

+

My Album Viewer

+
+ +
+
+ + + + + + + + + + + + + + + +
Album TitleAlbum YearAlbum RatingNumber of SongsArtist
212011A11Adele
+
+
+ + + \ No newline at end of file diff --git a/page-object/sample-application/src/main/resources/sample-ui/album-page.html b/page-object/sample-application/src/main/resources/sample-ui/album-page.html new file mode 100644 index 000000000000..5bf843be265d --- /dev/null +++ b/page-object/sample-application/src/main/resources/sample-ui/album-page.html @@ -0,0 +1,74 @@ + + + + + + Album Page + + + +
+

21

+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + +
Title:
Artist:
Year: + +
Rating:
Number of Songs:
+
+
+
+ + + \ No newline at end of file diff --git a/page-object/sample-application/src/main/resources/sample-ui/css/album-list.css b/page-object/sample-application/src/main/resources/sample-ui/css/album-list.css new file mode 100644 index 000000000000..9c828e1a0681 --- /dev/null +++ b/page-object/sample-application/src/main/resources/sample-ui/css/album-list.css @@ -0,0 +1,22 @@ +table { + font-size: 16px; + border-collapse: collapse; +} + +th { + background-color: #FFFFFF; + border: 1px solid black; + color: black; + width: 150px; + height: 20px; +} + +td { + border: 1px solid black; + background-color: white; +} + +th, td { + padding: 15px; + text-align: left; +} \ No newline at end of file diff --git a/page-object/sample-application/src/main/resources/sample-ui/css/style.css b/page-object/sample-application/src/main/resources/sample-ui/css/style.css new file mode 100644 index 000000000000..6efb32df9a1f --- /dev/null +++ b/page-object/sample-application/src/main/resources/sample-ui/css/style.css @@ -0,0 +1,3 @@ +body { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; +} \ No newline at end of file diff --git a/page-object/sample-application/src/main/resources/sample-ui/login.html b/page-object/sample-application/src/main/resources/sample-ui/login.html new file mode 100644 index 000000000000..1d77860572f2 --- /dev/null +++ b/page-object/sample-application/src/main/resources/sample-ui/login.html @@ -0,0 +1,48 @@ + + + + + + Login + + + +
+

Login

+
+ +
+
+ + + + +
Username:
Password:
+ +
+
+ + \ No newline at end of file diff --git a/page-object/test-automation/pom.xml b/page-object/test-automation/pom.xml new file mode 100644 index 000000000000..feac74bd9e39 --- /dev/null +++ b/page-object/test-automation/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + page-object + com.iluwatar + 1.12.0-SNAPSHOT + + test-automation + + + junit + junit + test + + + net.sourceforge.htmlunit + htmlunit + 2.22 + + + + diff --git a/page-object/test-automation/src/main/java/com/iluwatar/Page.java b/page-object/test-automation/src/main/java/com/iluwatar/Page.java new file mode 100644 index 000000000000..515872efc230 --- /dev/null +++ b/page-object/test-automation/src/main/java/com/iluwatar/Page.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +import com.gargoylesoftware.htmlunit.WebClient; + +/** + * Encapsulation for a generic 'Page' + */ +public abstract class Page { + + /** + * Application Under Test path + * This directory location is where html web pages are located + */ + public static final String AUT_PATH = "../sample-application/src/main/resources/sample-ui/"; + + protected WebClient webClient; + + /** + * Constructor + * + * @param webClient {@link WebClient} + */ + public Page(WebClient webClient) { + this.webClient = webClient; + } + + /** + * Checks that the current page is actually the page this page object represents + * + * @return true if so, otherwise false + */ + public abstract boolean isAt(); + + +} diff --git a/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java b/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java new file mode 100644 index 000000000000..00edc5688107 --- /dev/null +++ b/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java @@ -0,0 +1,95 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.iluwatar.Page; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.List; + +/** + * Page Object encapsulating the Album List page (album-list.html) + */ +public class AlbumListPage extends Page { + + private static final String ALBUM_LIST_HTML_FILE = "album-list.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + ALBUM_LIST_HTML_FILE; + + private HtmlPage page; + + private List albumLinks; + + + /** + * Constructor + */ + public AlbumListPage(WebClient webClient) { + super(webClient); + try { + page = this.webClient.getPage(PAGE_URL); + + // uses XPath to find list of html anchor tags with the class album in it + albumLinks = (List) page.getByXPath("//tr[@class='album']//a"); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Album List".equals(page.getTitleText()); + } + + /** + * Selects an album by the given album title + * + * @param albumTitle the title of the album to click + * @return the album page + */ + public AlbumPage selectAlbum(String albumTitle) { + for (HtmlAnchor anchor : albumLinks) { + if (anchor.getTextContent().equals(albumTitle)) { + try { + anchor.click(); + return new AlbumPage(webClient); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + throw new IllegalArgumentException("No links with the album title: " + albumTitle); + } + + +} diff --git a/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java b/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java new file mode 100644 index 000000000000..5f72be8fde4a --- /dev/null +++ b/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java @@ -0,0 +1,185 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlNumberInput; +import com.gargoylesoftware.htmlunit.html.HtmlOption; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSelect; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; +import com.iluwatar.Page; + +import java.io.IOException; +import java.net.MalformedURLException; + +/** + * Page Object encapsulating the Album Page (album-page.html) + */ +public class AlbumPage extends Page { + + private static final String ALBUM_PAGE_HTML_FILE = "album-page.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + ALBUM_PAGE_HTML_FILE; + + private HtmlPage page; + + private HtmlTextInput albumTitleInputTextField; + private HtmlTextInput artistInputTextField; + private HtmlSelect albumYearSelectOption; + private HtmlTextInput albumRatingInputTextField; + private HtmlNumberInput numberOfSongsNumberField; + + private HtmlSubmitInput cancelButton; + private HtmlSubmitInput saveButton; + + /** + * Constructor + */ + public AlbumPage(WebClient webClient) { + super(webClient); + try { + page = this.webClient.getPage(PAGE_URL); + initializeHtmlElements(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + + private void initializeHtmlElements() { + albumTitleInputTextField = (HtmlTextInput) page.getElementById("albumTitle"); + artistInputTextField = (HtmlTextInput) page.getElementById("albumArtist"); + albumYearSelectOption = (HtmlSelect) page.getElementById("albumYear"); + albumRatingInputTextField = (HtmlTextInput) page.getElementById("albumRating"); + numberOfSongsNumberField = (HtmlNumberInput) page.getElementById("numberOfSongs"); + + cancelButton = (HtmlSubmitInput) page.getElementById("cancelButton"); + saveButton = (HtmlSubmitInput) page.getElementById("saveButton"); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Album Page".equals(page.getTitleText()); + } + + + /** + * Sets the album title input text field + * + * @param albumTitle the new album title value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumTitle(String albumTitle) { + albumTitleInputTextField.setText(albumTitle); + return this; + } + + + /** + * Sets the artist input text field + * + * @param artist the new artist value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeArtist(String artist) { + artistInputTextField.setText(artist); + return this; + } + + + /** + * Selects the select's option value based on the year value given + * + * @param year the new year value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumYear(int year) { + HtmlOption yearOption = albumYearSelectOption.getOptionByValue(Integer.toString(year)); + albumYearSelectOption.setSelectedAttribute(yearOption, true); + return this; + } + + + /** + * Sets the album rating input text field + * + * @param albumRating the new album rating value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumRating(String albumRating) { + albumRatingInputTextField.setText(albumRating); + return this; + } + + /** + * Sets the number of songs number input field + * + * @param numberOfSongs the new number of songs value to be set + * @return {@link AlbumPage} + */ + public AlbumPage changeNumberOfSongs(int numberOfSongs) { + numberOfSongsNumberField.setText(Integer.toString(numberOfSongs)); + return this; + } + + + /** + * Cancel changes made by clicking the cancel button + * + * @return {@link AlbumListPage} + */ + public AlbumListPage cancelChanges() { + try { + cancelButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return new AlbumListPage(webClient); + } + + + /** + * Saves changes made by clicking the save button + * + * @return {@link AlbumPage} + */ + public AlbumPage saveChanges() { + try { + saveButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + +} diff --git a/page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java b/page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java new file mode 100644 index 000000000000..19810f354cd6 --- /dev/null +++ b/page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; +import com.iluwatar.Page; +import java.io.IOException; +import java.net.MalformedURLException; + +/** + * Page Object encapsulating the Login Page (login.html) + */ +public class LoginPage extends Page { + + private static final String LOGIN_PAGE_HTML_FILE = "login.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + LOGIN_PAGE_HTML_FILE; + + private HtmlPage page; + + private HtmlTextInput usernameInputTextField; + private HtmlPasswordInput passwordInputPasswordField; + private HtmlSubmitInput loginButton; + + + /** + * Constructor + * + * @param webClient {@link WebClient} + */ + public LoginPage(WebClient webClient) { + super(webClient); + try { + page = this.webClient.getPage(PAGE_URL); + + usernameInputTextField = (HtmlTextInput) page.getElementById("username"); + passwordInputPasswordField = (HtmlPasswordInput) page.getElementById("password"); + loginButton = (HtmlSubmitInput) page.getElementById("loginButton"); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Login".equals(page.getTitleText()); + } + + + /** + * Enters the username into the username input text field + * + * @param username the username to enter + * @return {@link LoginPage} + */ + public LoginPage enterUsername(String username) { + usernameInputTextField.setText(username); + return this; + } + + + /** + * Enters the password into the password input password field + * + * @param password the password to enter + * @return {@link LoginPage} + */ + public LoginPage enterPassword(String password) { + passwordInputPasswordField.setText(password); + return this; + } + + + /** + * Clicking on the login button to 'login' + * + * @return {@link AlbumListPage} + * - this is the page that user gets navigated to once successfully logged in + */ + public AlbumListPage login() { + try { + loginButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return new AlbumListPage(webClient); + } + + +} diff --git a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java b/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java new file mode 100644 index 000000000000..3b420e366846 --- /dev/null +++ b/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pages.AlbumListPage; +import com.iluwatar.pages.AlbumPage; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +public class AlbumListPageTest { + + @Test + public void testSelectAlbum() { + AlbumListPage albumListPage = new AlbumListPage(new WebClient()); + + AlbumPage albumPage = albumListPage.selectAlbum("21"); + + assertTrue(albumPage.isAt()); + } + +} diff --git a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java b/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java new file mode 100644 index 000000000000..8ca464165934 --- /dev/null +++ b/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pages.AlbumListPage; +import com.iluwatar.pages.AlbumPage; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class AlbumPageTest { + + private AlbumPage albumPage = new AlbumPage(new WebClient()); + + @Test + public void testSaveAlbum() { + + AlbumPage albumPageAfterChanges = albumPage.changeAlbumTitle("25") + .changeArtist("Adele Laurie Blue Adkins") + .changeAlbumYear(2015) + .changeAlbumRating("B") + .changeNumberOfSongs(20) + .saveChanges(); + + assertTrue(albumPageAfterChanges.isAt()); + + } + + @Test + public void testCancelChanges() { + AlbumListPage albumListPage = albumPage.cancelChanges(); + assertTrue(albumListPage.isAt()); + } + +} diff --git a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java b/page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java new file mode 100644 index 000000000000..322a4b97c5a2 --- /dev/null +++ b/page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pages.AlbumListPage; +import com.iluwatar.pages.LoginPage; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class LoginPageTest { + + @Test + public void testLogin() { + LoginPage loginPage = new LoginPage(new WebClient()); + + AlbumListPage albumListPage = loginPage.enterUsername("admin") + .enterPassword("password") + .login(); + + assertTrue(albumListPage.isAt()); + } + +} diff --git a/pom.xml b/pom.xml index 0e3318fb6050..0e28a80817d9 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ UTF-8 5.0.1.Final 4.2.4.RELEASE - 1.3.3.RELEASE + 1.3.3.RELEASE 1.9.2.RELEASE 1.4.190 4.12 @@ -126,6 +126,7 @@ semaphore hexagonal abstract-document + page-object From c8f04d2f0d1ea59404eb713785d26e939a572d74 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sat, 18 Jun 2016 19:20:29 +0100 Subject: [PATCH 090/207] added Class Diagram --- page-object/etc/page-object.png | Bin 0 -> 78043 bytes page-object/etc/page-object.ucls | 60 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 page-object/etc/page-object.png create mode 100644 page-object/etc/page-object.ucls diff --git a/page-object/etc/page-object.png b/page-object/etc/page-object.png new file mode 100644 index 0000000000000000000000000000000000000000..27b99b4490631740c2fe68c119290435c6db08f0 GIT binary patch literal 78043 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

rOge6I`XBAL3}}erSA|U;!qiat9!h8E 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0282d6663f06816da33b7dd4e919018ab56bf742 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sat, 18 Jun 2016 21:08:15 +0100 Subject: [PATCH 091/207] README --- page-object/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 page-object/README.md diff --git a/page-object/README.md b/page-object/README.md new file mode 100644 index 000000000000..e66e184c336c --- /dev/null +++ b/page-object/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Page Object +folder: page-object +permalink: /patterns/page-object/ +categories: Testing +tags: +- Testing +- Web Development +- Encapsulation +--- + +## Intent + +Page Object encapsulates the UI, hiding the underlying UI widgetry of an application (commonly a web application) and providing an application-specific API to allow the manipulation of UI components required for tests. In doing so, it allows the test class itself to focus on the test logic instead. + + +![alt text](./etc/page-object.png "Page Object") + + +## Applicability + +Use the Page Object pattern when + +* You are writing automated tests for your web application and you want to separate the UI manipulation required for the tests from the actual test logic. +* Make your tests less brittle, and more readable and robust + +## Credits + +* [Martin Fowler - PageObject](http://martinfowler.com/bliki/PageObject.html) +* [Selenium - Page Objects](https://github.com/SeleniumHQ/selenium/wiki/PageObjects) + From 6677d1091d5a06d696cb0fd96d922e8db3f2cc73 Mon Sep 17 00:00:00 2001 From: protopapa Date: Thu, 23 Jun 2016 22:54:44 +0200 Subject: [PATCH 092/207] implementation of microservices architectural pattern #296 --- aggregator-microservices/README.md | 29 ++++++ .../aggregator-service/pom.xml | 88 ++++++++++++++++++ .../aggregator/microservices/Aggregator.java | 58 ++++++++++++ .../aggregator/microservices/App.java | 39 ++++++++ .../aggregator/microservices/Product.java | 57 ++++++++++++ .../ProductInformationClient.java | 32 +++++++ .../ProductInformationClientImpl.java | 51 ++++++++++ .../microservices/ProductInventoryClient.java | 31 ++++++ .../ProductInventoryClientImpl.java | 51 ++++++++++ .../src/main/resources/application.properties | 24 +++++ .../microservices/AggregatorTest.java | 67 +++++++++++++ .../etc/aggregator-microservice.png | Bin 0 -> 41304 bytes .../information-microservice/pom.xml | 79 ++++++++++++++++ .../microservice/InformationApplication.java | 37 ++++++++ .../microservice/InformationController.java | 41 ++++++++ .../src/main/resources/application.properties | 24 +++++ .../InformationControllerTest.java | 39 ++++++++ .../inventory-microservice/pom.xml | 79 ++++++++++++++++ .../microservice/InventoryApplication.java | 38 ++++++++ .../microservice/InventoryController.java | 42 +++++++++ .../src/main/resources/application.properties | 24 +++++ .../microservice/InventoryControllerTest.java | 38 ++++++++ aggregator-microservices/pom.xml | 43 +++++++++ pom.xml | 1 + 24 files changed, 1012 insertions(+) create mode 100644 aggregator-microservices/README.md create mode 100644 aggregator-microservices/aggregator-service/pom.xml create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java create mode 100644 aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java create mode 100644 aggregator-microservices/aggregator-service/src/main/resources/application.properties create mode 100644 aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java create mode 100644 aggregator-microservices/etc/aggregator-microservice.png create mode 100644 aggregator-microservices/information-microservice/pom.xml create mode 100644 aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java create mode 100644 aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java create mode 100644 aggregator-microservices/information-microservice/src/main/resources/application.properties create mode 100644 aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java create mode 100644 aggregator-microservices/inventory-microservice/pom.xml create mode 100644 aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java create mode 100644 aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java create mode 100644 aggregator-microservices/inventory-microservice/src/main/resources/application.properties create mode 100644 aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java create mode 100644 aggregator-microservices/pom.xml diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md new file mode 100644 index 000000000000..e65f26d9a38a --- /dev/null +++ b/aggregator-microservices/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Aggregator Microservices +folder: aggregator-microservices +permalink: /patterns/aggregator-microservices/ +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. + +![alt text](./etc/aggregator-microservice.png "Aggregator Microservice") + +## 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 new file mode 100644 index 000000000000..5a3490559c40 --- /dev/null +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -0,0 +1,88 @@ + + + + + aggregator-microservices + com.iluwatar + 1.12.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 new file mode 100644 index 000000000000..639349db9499 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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 new file mode 100644 index 000000000000..25bb9ee6fc8a --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +@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 new file mode 100644 index 000000000000..edf0c121db92 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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/ProductInformationClient.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java new file mode 100644 index 000000000000..863db4759206 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java @@ -0,0 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +/** + * Interface for the Information micro-service. + */ +public interface ProductInformationClient { + + String getProductTitle(); + +} 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 new file mode 100644 index 000000000000..580875c126b7 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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 java.io.IOException; + +/** + * An adapter to communicate with information micro-service. + */ +public class ProductInformationClientImpl implements ProductInformationClient { + + @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) { + e.printStackTrace(); + } + return response; + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java new file mode 100644 index 000000000000..22be900b3bac --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +/** + * Interface to Inventory micro-service. + */ +public interface ProductInventoryClient { + + int getProductInventories(); +} 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 new file mode 100644 index 000000000000..e399fb83690b --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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 java.io.IOException; + +/** + * An adapter to communicate with inventory micro-service. + */ +public class ProductInventoryClientImpl implements ProductInventoryClient { + + @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) { + e.printStackTrace(); + } + 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 new file mode 100644 index 000000000000..69f58171237a --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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 new file mode 100644 index 000000000000..95d36fe25933 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 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.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +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()); + } + +} \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-microservice.png b/aggregator-microservices/etc/aggregator-microservice.png new file mode 100644 index 0000000000000000000000000000000000000000..ad344a7e1e0fa72633fb06e53ae4ddc0a0695ae4 GIT binary patch literal 41304 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˂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

+0z( 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 literal 0 HcmV?d00001 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml new file mode 100644 index 000000000000..844c2a3d7072 --- /dev/null +++ b/aggregator-microservices/information-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + aggregator-microservices + com.iluwatar + 1.12.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/java/com/iluwatar/information/microservice/InformationApplication.java b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java new file mode 100644 index 000000000000..c93219c17bdd --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Inventory Application starts container (Spring Boot) and exposes the Inventory micro-service. + */ +@SpringBootApplication +public class InformationApplication { + + public static void main(String[] args) { + SpringApplication.run(InformationApplication.class, args); + } +} diff --git a/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java new file mode 100644 index 000000000000..37ec45c1be65 --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 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.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class InformationController { + + /** + * Endpoint to retrieve a product's informations. + * + * @return product inventory. + */ + @RequestMapping(value = "/information", method = RequestMethod.GET) + public String getProductTitle() { + return "The Product Title."; + } +} diff --git a/aggregator-microservices/information-microservice/src/main/resources/application.properties b/aggregator-microservices/information-microservice/src/main/resources/application.properties new file mode 100644 index 000000000000..3c8452f1f3ea --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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 new file mode 100644 index 000000000000..9c3a6b98de9d --- /dev/null +++ b/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +public class InformationControllerTest { + + @Test + public void shouldGetProductTitle() { + InformationController infoController = new InformationController(); + + String title = infoController.getProductTitle(); + + Assert.assertEquals("The Product Title.", title); + } + +} \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml new file mode 100644 index 000000000000..9fee7db5b61c --- /dev/null +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + aggregator-microservices + com.iluwatar + 1.12.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/java/com/iluwatar/inventory/microservice/InventoryApplication.java b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java new file mode 100644 index 000000000000..3e2cf9e601d4 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Inventory Application starts container (Spring Boot) and exposes the Inventory micro-service. + */ +@SpringBootApplication +public class InventoryApplication { + + public static void main(String[] args) { + SpringApplication.run(InventoryApplication.class, args); + } + +} diff --git a/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java new file mode 100644 index 000000000000..483e28bf329c --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 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.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class InventoryController { + + /** + * Endpoint to retrieve a product's inventories. + * + * @return product inventory. + */ + @RequestMapping(value = "/inventories", method = RequestMethod.GET) + public int getProductInventories() { + return 5; + } + +} diff --git a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties new file mode 100644 index 000000000000..36f7589fc243 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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 new file mode 100644 index 000000000000..787ec0e611d5 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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; + +public class InventoryControllerTest { + + @Test + public void testGetProductInventories() throws Exception { + InventoryController inventoryController = new InventoryController(); + + int numberOfInventories = inventoryController.getProductInventories(); + + Assert.assertEquals(5, numberOfInventories); + } +} \ No newline at end of file diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml new file mode 100644 index 000000000000..d2f0a40dd0b7 --- /dev/null +++ b/aggregator-microservices/pom.xml @@ -0,0 +1,43 @@ + + + + + java-design-patterns + com.iluwatar + 1.12.0-SNAPSHOT + + 4.0.0 + aggregator-microservices + pom + + + information-microservice + aggregator-service + inventory-microservice + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0e3318fb6050..9469941f51f1 100644 --- a/pom.xml +++ b/pom.xml @@ -126,6 +126,7 @@ semaphore hexagonal abstract-document + aggregator-microservices From 27bbbd833588c069d1b7af8d9548a35c548c4323 Mon Sep 17 00:00:00 2001 From: protopapa Date: Sun, 26 Jun 2016 14:57:27 +0200 Subject: [PATCH 093/207] fix bug --- .../aggregator/microservices/ProductInformationClientImpl.java | 2 ++ .../aggregator/microservices/ProductInventoryClientImpl.java | 2 ++ 2 files changed, 4 insertions(+) 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 index 580875c126b7..1c5c1527c459 100644 --- 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 @@ -27,12 +27,14 @@ 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 information micro-service. */ +@Component public class ProductInformationClientImpl implements ProductInformationClient { @Override 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 index e399fb83690b..14d0a32c421c 100644 --- 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 @@ -27,12 +27,14 @@ 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 inventory micro-service. */ +@Component public class ProductInventoryClientImpl implements ProductInventoryClient { @Override From e7bd3cade2b0ba8c1a756367fbf9e91489e68ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 26 Jun 2016 20:23:05 +0300 Subject: [PATCH 094/207] Update link for Naked Objects paper --- naked-objects/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/naked-objects/README.md b/naked-objects/README.md index eb1c083b14e2..66e6ac2b0224 100644 --- a/naked-objects/README.md +++ b/naked-objects/README.md @@ -29,4 +29,4 @@ Use the Naked Objects pattern when ## Credits -* [Richard Pawson - Naked Objects](http://downloads.nakedobjects.net/resources/Pawson%20thesis.pdf) +* [Richard Pawson - Naked Objects](https://isis.apache.org/resources/thesis/Pawson-Naked-Objects-thesis.pdf) From 27e4e9afee10dc6306fcb511bfa30092f02b5222 Mon Sep 17 00:00:00 2001 From: Colin But Date: Tue, 28 Jun 2016 21:53:55 +0100 Subject: [PATCH 095/207] Feedback changes - restructed project structure --- page-object/etc/page-object.ucls | 8 ++-- page-object/pom.xml | 17 +++++-- page-object/sample-application/pom.xml | 35 -------------- .../java/com/iluwatar/pageobject/App.java | 2 +- .../src/main/resources/credentials.txt | 0 .../main/resources/sample-ui/album-list.html | 0 .../main/resources/sample-ui/album-page.html | 0 .../resources/sample-ui/css/album-list.css | 0 .../main/resources/sample-ui/css/style.css | 0 .../src/main/resources/sample-ui/login.html | 0 .../pageobject/AlbumListPageTest.java | 4 +- .../iluwatar/pageobject/AlbumPageTest.java | 4 +- .../iluwatar/pageobject/LoginPageTest.java | 4 +- .../pageobject}/pages/AlbumListPage.java | 3 +- .../iluwatar/pageobject}/pages/AlbumPage.java | 3 +- .../iluwatar/pageobject}/pages/LoginPage.java | 3 +- .../com/iluwatar/pageobject/pages}/Page.java | 4 +- page-object/test-automation/pom.xml | 48 ------------------- 18 files changed, 28 insertions(+), 107 deletions(-) delete mode 100644 page-object/sample-application/pom.xml rename page-object/{sample-application => }/src/main/java/com/iluwatar/pageobject/App.java (97%) rename page-object/{sample-application => }/src/main/resources/credentials.txt (100%) rename page-object/{sample-application => }/src/main/resources/sample-ui/album-list.html (100%) rename page-object/{sample-application => }/src/main/resources/sample-ui/album-page.html (100%) rename page-object/{sample-application => }/src/main/resources/sample-ui/css/album-list.css (100%) rename page-object/{sample-application => }/src/main/resources/sample-ui/css/style.css (100%) rename page-object/{sample-application => }/src/main/resources/sample-ui/login.html (100%) rename page-object/{test-automation => }/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java (93%) rename page-object/{test-automation => }/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java (94%) rename page-object/{test-automation => }/src/test/java/com/iluwatar/pageobject/LoginPageTest.java (94%) rename page-object/{test-automation/src/main/java/com/iluwatar => src/test/java/com/iluwatar/pageobject}/pages/AlbumListPage.java (98%) rename page-object/{test-automation/src/main/java/com/iluwatar => src/test/java/com/iluwatar/pageobject}/pages/AlbumPage.java (99%) rename page-object/{test-automation/src/main/java/com/iluwatar => src/test/java/com/iluwatar/pageobject}/pages/LoginPage.java (98%) rename page-object/{test-automation/src/main/java/com/iluwatar => src/test/java/com/iluwatar/pageobject/pages}/Page.java (93%) delete mode 100644 page-object/test-automation/pom.xml diff --git a/page-object/etc/page-object.ucls b/page-object/etc/page-object.ucls index 2d6bc07220a5..2e633af436c2 100644 --- a/page-object/etc/page-object.ucls +++ b/page-object/etc/page-object.ucls @@ -1,7 +1,7 @@ - - - - 1.12.0-SNAPSHOT page-object - pom - - sample-application - test-automation - + + + junit + junit + test + + + net.sourceforge.htmlunit + htmlunit + 2.22 + + diff --git a/page-object/sample-application/pom.xml b/page-object/sample-application/pom.xml deleted file mode 100644 index 74116fdb3408..000000000000 --- a/page-object/sample-application/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - 4.0.0 - - page-object - com.iluwatar - 1.12.0-SNAPSHOT - - sample-application - diff --git a/page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java b/page-object/src/main/java/com/iluwatar/pageobject/App.java similarity index 97% rename from page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java rename to page-object/src/main/java/com/iluwatar/pageobject/App.java index 155097c73c8f..67b3f947b653 100644 --- a/page-object/sample-application/src/main/java/com/iluwatar/pageobject/App.java +++ b/page-object/src/main/java/com/iluwatar/pageobject/App.java @@ -71,7 +71,7 @@ public static void main(String[] args) { String currentWorkingDir = System.getProperty("user.dir"); File applicationFile = new File(currentWorkingDir - + "/page-object/sample-application/src/main/resources/sample-ui/login.html"); + + "/page-object/src/main/resources/sample-ui/login.html"); // should work for unix like OS (mac, unix etc...) if (Desktop.isDesktopSupported()) { diff --git a/page-object/sample-application/src/main/resources/credentials.txt b/page-object/src/main/resources/credentials.txt similarity index 100% rename from page-object/sample-application/src/main/resources/credentials.txt rename to page-object/src/main/resources/credentials.txt diff --git a/page-object/sample-application/src/main/resources/sample-ui/album-list.html b/page-object/src/main/resources/sample-ui/album-list.html similarity index 100% rename from page-object/sample-application/src/main/resources/sample-ui/album-list.html rename to page-object/src/main/resources/sample-ui/album-list.html diff --git a/page-object/sample-application/src/main/resources/sample-ui/album-page.html b/page-object/src/main/resources/sample-ui/album-page.html similarity index 100% rename from page-object/sample-application/src/main/resources/sample-ui/album-page.html rename to page-object/src/main/resources/sample-ui/album-page.html diff --git a/page-object/sample-application/src/main/resources/sample-ui/css/album-list.css b/page-object/src/main/resources/sample-ui/css/album-list.css similarity index 100% rename from page-object/sample-application/src/main/resources/sample-ui/css/album-list.css rename to page-object/src/main/resources/sample-ui/css/album-list.css diff --git a/page-object/sample-application/src/main/resources/sample-ui/css/style.css b/page-object/src/main/resources/sample-ui/css/style.css similarity index 100% rename from page-object/sample-application/src/main/resources/sample-ui/css/style.css rename to page-object/src/main/resources/sample-ui/css/style.css diff --git a/page-object/sample-application/src/main/resources/sample-ui/login.html b/page-object/src/main/resources/sample-ui/login.html similarity index 100% rename from page-object/sample-application/src/main/resources/sample-ui/login.html rename to page-object/src/main/resources/sample-ui/login.html diff --git a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java similarity index 93% rename from page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java rename to page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java index 3b420e366846..5afc05f5593d 100644 --- a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java @@ -24,8 +24,8 @@ import com.gargoylesoftware.htmlunit.WebClient; -import com.iluwatar.pages.AlbumListPage; -import com.iluwatar.pages.AlbumPage; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.AlbumPage; import org.junit.Test; import static org.junit.Assert.assertTrue; diff --git a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java similarity index 94% rename from page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java rename to page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java index 8ca464165934..811ffd02a2ca 100644 --- a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java @@ -23,8 +23,8 @@ package com.iluwatar.pageobject; import com.gargoylesoftware.htmlunit.WebClient; -import com.iluwatar.pages.AlbumListPage; -import com.iluwatar.pages.AlbumPage; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.AlbumPage; import org.junit.Test; import static org.junit.Assert.assertTrue; diff --git a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java similarity index 94% rename from page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java rename to page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java index 322a4b97c5a2..b9e8e782df3b 100644 --- a/page-object/test-automation/src/test/java/com/iluwatar/pageobject/LoginPageTest.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java @@ -23,8 +23,8 @@ package com.iluwatar.pageobject; import com.gargoylesoftware.htmlunit.WebClient; -import com.iluwatar.pages.AlbumListPage; -import com.iluwatar.pages.LoginPage; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.LoginPage; import org.junit.Test; import static org.junit.Assert.assertTrue; diff --git a/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java similarity index 98% rename from page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java rename to page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java index 00edc5688107..7f4e50fb5834 100644 --- a/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumListPage.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java @@ -20,12 +20,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.pages; +package com.iluwatar.pageobject.pages; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlAnchor; import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.iluwatar.Page; import java.io.IOException; import java.net.MalformedURLException; diff --git a/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java similarity index 99% rename from page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java rename to page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java index 5f72be8fde4a..b2df6ef33e00 100644 --- a/page-object/test-automation/src/main/java/com/iluwatar/pages/AlbumPage.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.pages; +package com.iluwatar.pageobject.pages; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlNumberInput; @@ -29,7 +29,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlSelect; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import com.gargoylesoftware.htmlunit.html.HtmlTextInput; -import com.iluwatar.Page; import java.io.IOException; import java.net.MalformedURLException; diff --git a/page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java similarity index 98% rename from page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java rename to page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java index 19810f354cd6..ac12f720f394 100644 --- a/page-object/test-automation/src/main/java/com/iluwatar/pages/LoginPage.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java @@ -20,14 +20,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.pages; +package com.iluwatar.pageobject.pages; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import com.gargoylesoftware.htmlunit.html.HtmlTextInput; -import com.iluwatar.Page; import java.io.IOException; import java.net.MalformedURLException; diff --git a/page-object/test-automation/src/main/java/com/iluwatar/Page.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java similarity index 93% rename from page-object/test-automation/src/main/java/com/iluwatar/Page.java rename to page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java index 515872efc230..340a728006ac 100644 --- a/page-object/test-automation/src/main/java/com/iluwatar/Page.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar; +package com.iluwatar.pageobject.pages; import com.gargoylesoftware.htmlunit.WebClient; @@ -33,7 +33,7 @@ public abstract class Page { * Application Under Test path * This directory location is where html web pages are located */ - public static final String AUT_PATH = "../sample-application/src/main/resources/sample-ui/"; + public static final String AUT_PATH = "src/main/resources/sample-ui/"; protected WebClient webClient; diff --git a/page-object/test-automation/pom.xml b/page-object/test-automation/pom.xml deleted file mode 100644 index feac74bd9e39..000000000000 --- a/page-object/test-automation/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - 4.0.0 - - page-object - com.iluwatar - 1.12.0-SNAPSHOT - - test-automation - - - junit - junit - test - - - net.sourceforge.htmlunit - htmlunit - 2.22 - - - - From 8efaf441707d55f442703194568b13111392ed9f Mon Sep 17 00:00:00 2001 From: Colin But Date: Tue, 28 Jun 2016 22:02:03 +0100 Subject: [PATCH 096/207] Feedback changes - sort out dependency --- page-object/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/page-object/pom.xml b/page-object/pom.xml index 537fdb19706f..47c13bcc580b 100644 --- a/page-object/pom.xml +++ b/page-object/pom.xml @@ -41,7 +41,6 @@ net.sourceforge.htmlunit htmlunit - 2.22 From c212ee5cb1df6a8316b3fd778529f8d1e37a7337 Mon Sep 17 00:00:00 2001 From: Colin But Date: Tue, 28 Jun 2016 22:03:57 +0100 Subject: [PATCH 097/207] Feedback changes - fix dependency in pom --- pom.xml | 801 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 405 insertions(+), 396 deletions(-) diff --git a/pom.xml b/pom.xml index 80c7e9cb0a1d..986dc493e396 100644 --- a/pom.xml +++ b/pom.xml @@ -17,414 +17,423 @@ 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. ---> - 4.0.0 +--> + + 4.0.0 - com.iluwatar - java-design-patterns - 1.12.0-SNAPSHOT - pom + com.iluwatar + java-design-patterns + 1.12.0-SNAPSHOT + pom - 2014 + 2014 - - UTF-8 - 5.0.1.Final - 4.2.4.RELEASE - 1.3.3.RELEASE - 1.9.2.RELEASE - 1.4.190 - 4.12 - 3.0 - 4.0.0 - 0.7.2.201409121644 - 1.4 - 2.16.1 - 1.2.17 - 19.0 - 1.15.1 - 1.10.19 - 4.12.1 - 4.5.2 - - - abstract-factory - builder - factory-method - prototype - singleton - adapter - bridge - composite - dao - data-mapper - decorator - facade - flyweight - proxy - chain - command - interpreter - iterator - mediator - memento - model-view-presenter - observer - state - strategy - template-method - visitor - double-checked-locking - servant - service-locator - null-object - event-aggregator - callback - execute-around - property - intercepting-filter - producer-consumer - poison-pill - reader-writer-lock - lazy-loading - service-layer - specification - tolerant-reader - model-view-controller - flux - double-dispatch - multiton - resource-acquisition-is-initialization - thread-pool - twin - private-class-data - object-pool - dependency-injection - naked-objects - front-controller - repository - async-method-invocation - monostate - step-builder - business-delegate - half-sync-half-async - layers - message-channel - fluentinterface - reactor - caching - publish-subscribe - delegation - event-driven-architecture - api-gateway - factory-kit - feature-toggle - value-object - monad - mute-idiom - mutex - semaphore - hexagonal - abstract-document - aggregator-microservices - page-object - + + UTF-8 + 5.0.1.Final + 4.2.4.RELEASE + 1.3.3.RELEASE + 1.9.2.RELEASE + 1.4.190 + 4.12 + 3.0 + 4.0.0 + 0.7.2.201409121644 + 1.4 + 2.16.1 + 1.2.17 + 19.0 + 1.15.1 + 1.10.19 + 4.12.1 + 4.5.2 + 2.22 + + + abstract-factory + builder + factory-method + prototype + singleton + adapter + bridge + composite + dao + data-mapper + decorator + facade + flyweight + proxy + chain + command + interpreter + iterator + mediator + memento + model-view-presenter + observer + state + strategy + template-method + visitor + double-checked-locking + servant + service-locator + null-object + event-aggregator + callback + execute-around + property + intercepting-filter + producer-consumer + poison-pill + reader-writer-lock + lazy-loading + service-layer + specification + tolerant-reader + model-view-controller + flux + double-dispatch + multiton + resource-acquisition-is-initialization + thread-pool + twin + private-class-data + object-pool + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + monostate + step-builder + business-delegate + half-sync-half-async + layers + message-channel + fluentinterface + reactor + caching + publish-subscribe + delegation + event-driven-architecture + api-gateway + factory-kit + feature-toggle + value-object + monad + mute-idiom + mutex + semaphore + hexagonal + abstract-document + aggregator-microservices + page-object + - - - - org.hibernate - hibernate-core - ${hibernate.version} - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.springframework - spring-test - ${spring.version} - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - org.springframework.data - spring-data-jpa - ${spring-data.version} - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.apache.httpcomponents - httpclient - ${apache-httpcomponents.version} - - - com.h2database - h2 - ${h2.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.apache.camel - camel-core - ${camel.version} - - - org.apache.camel - camel-stream - ${camel.version} - - - junit - junit - ${junit.version} - test - - - org.mockito - mockito-core - ${mockito.version} - test - - - log4j - log4j - ${log4j.version} - - - com.google.guava - guava - ${guava.version} - - - com.github.stefanbirkner - system-rules - ${systemrules.version} - test - - - de.bechte.junit - junit-hierarchicalcontextrunner - ${hierarchical-junit-runner-version} - test - - - + + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + org.springframework + spring-test + ${spring.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.apache.httpcomponents + httpclient + ${apache-httpcomponents.version} + + + com.h2database + h2 + ${h2.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + log4j + log4j + ${log4j.version} + + + com.google.guava + guava + ${guava.version} + + + com.github.stefanbirkner + system-rules + ${systemrules.version} + test + + + de.bechte.junit + junit-hierarchicalcontextrunner + ${hierarchical-junit-runner-version} + test + + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + + + - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - - jacoco-maven-plugin - - - [0.6.2,) - - - prepare-agent - - - - - - - - - - - - + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + + jacoco-maven-plugin + + + [0.6.2,) + + + prepare-agent + + + + + + + + + + + + - - - - org.apache.maven.plugins - maven-compiler-plugin - ${compiler.version} - - 1.8 - 1.8 - - - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - - - - - prepare-agent - - prepare-agent - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.version} + + 1.8 + 1.8 + + + + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls.version} + + jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + + domainapp/dom/modules/simple/QSimpleObject.class + + + + + prepare-agent + + prepare-agent + + + + - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - - - validate - - check - - validate - - checkstyle.xml - checkstyle-suppressions.xml - UTF-8 - true - true - true - - - - + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + validate + + check + + validate + + checkstyle.xml + checkstyle-suppressions.xml + UTF-8 + true + true + true + + + + - - org.jacoco - jacoco-maven-plugin - 0.7.5.201505241946 - - - - prepare-agent - - - - report - prepare-package - - report - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18.1 - - - org.apache.maven.surefire - surefire-junit47 - 2.18.1 - - - - -Xmx1024M ${argLine} - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - true - 5 - true - - - - - check - - - exclude-pmd.properties - - - + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.surefire + surefire-junit47 + 2.18.1 + + + + -Xmx1024M ${argLine} + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + true + 5 + true + + + + + check + + + exclude-pmd.properties + + + - - com.mycila - license-maven-plugin - 2.11 - -

com/mycila/maven/plugin/license/templates/MIT.txt
- - Ilkka Seppälä - - true - - - - install-format - install - - format - - - - - - + + com.mycila + license-maven-plugin + 2.11 + +
com/mycila/maven/plugin/license/templates/MIT.txt
+ + Ilkka Seppälä + + true +
+ + + install-format + install + + format + + + +
+ + - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - - + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + + From 4eac37c6b50176ec4c430b5250d885f68c10687c Mon Sep 17 00:00:00 2001 From: Colin But Date: Tue, 28 Jun 2016 22:04:39 +0100 Subject: [PATCH 098/207] Feedback changes - removed file --- .../src/main/resources/credentials.txt | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 page-object/src/main/resources/credentials.txt diff --git a/page-object/src/main/resources/credentials.txt b/page-object/src/main/resources/credentials.txt deleted file mode 100644 index 6f1bf2fb6b35..000000000000 --- a/page-object/src/main/resources/credentials.txt +++ /dev/null @@ -1,25 +0,0 @@ -==== - The MIT License - Copyright (c) 2014 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. -==== - -username - admin -password - password \ No newline at end of file From 7a118e4209bbb2d57f8fbc7936efeb86f05ba5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 30 Jun 2016 18:14:41 +0300 Subject: [PATCH 099/207] Reached milestone 1.12.0 --- abstract-document/pom.xml | 2 +- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- aggregator-microservices/aggregator-service/pom.xml | 2 +- aggregator-microservices/information-microservice/pom.xml | 2 +- aggregator-microservices/inventory-microservice/pom.xml | 2 +- aggregator-microservices/pom.xml | 2 +- api-gateway/api-gateway-service/pom.xml | 2 +- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- caching/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- data-mapper/pom.xml | 2 +- decorator/pom.xml | 2 +- delegation/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- event-driven-architecture/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-kit/pom.xml | 2 +- factory-method/pom.xml | 2 +- feature-toggle/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- hexagonal/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- monad/pom.xml | 2 +- monostate/pom.xml | 2 +- multiton/pom.xml | 2 +- mute-idiom/pom.xml | 2 +- mutex/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- producer-consumer/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- publish-subscribe/pom.xml | 2 +- reactor/pom.xml | 2 +- reader-writer-lock/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- semaphore/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- twin/pom.xml | 2 +- value-object/pom.xml | 2 +- visitor/pom.xml | 2 +- 90 files changed, 93 insertions(+), 93 deletions(-) diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index e7fc58047a97..36f014e67f1c 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 abstract-document diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index 734020720542..668ebbbd1317 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index 476eb4dce898..f8e550acd919 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 adapter diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index 5a3490559c40..8b8156e1b3a6 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index 844c2a3d7072..1c806e1fe3a9 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index 9fee7db5b61c..b8420895261d 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index d2f0a40dd0b7..bf20ece3381c 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 aggregator-microservices diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index 4e4714c56877..a99214ab30cf 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 api-gateway-service diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index b5f2f504c1a3..414a3d97e478 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 73b2326bdbd9..870980cad77b 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index a39fde8a61f8..db9061126c2e 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index 292eb136b9c4..7bab122e3d8e 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index 6ac261286c70..740fa659a860 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 bridge diff --git a/builder/pom.xml b/builder/pom.xml index 7d83f6c2871c..676d2f4299c7 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 3834b4c8fee4..70f7a8889198 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 business-delegate diff --git a/caching/pom.xml b/caching/pom.xml index 7b1b2235473d..9e1e95c9675b 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 caching diff --git a/callback/pom.xml b/callback/pom.xml index 8615c4653e57..594d91b52019 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 callback diff --git a/chain/pom.xml b/chain/pom.xml index a1cbffcb2479..202e29eba20d 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 chain diff --git a/command/pom.xml b/command/pom.xml index bb5cd78ff053..fca18c3fd779 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 command diff --git a/composite/pom.xml b/composite/pom.xml index 6513a104e900..6dfb47d38f67 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 composite diff --git a/dao/pom.xml b/dao/pom.xml index 26c35abb0d7d..1b56f0ed041f 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 dao diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index f65b647fa939..8e2845fd7f23 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 data-mapper diff --git a/decorator/pom.xml b/decorator/pom.xml index 58f2bb0bacc2..adda649a88fc 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 decorator diff --git a/delegation/pom.xml b/delegation/pom.xml index 33592dd829e3..a2c9c82d241e 100644 --- a/delegation/pom.xml +++ b/delegation/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index d8be9fcffd53..b5727803d1c1 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index f8ac5fa2e907..c98e82ec3a48 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -27,7 +27,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 8896abd9ff8f..77a456bbe521 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 1b1f289b27d1..5517934bb84c 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 event-aggregator diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml index cccf3908826d..910654fc6bc8 100644 --- a/event-driven-architecture/pom.xml +++ b/event-driven-architecture/pom.xml @@ -31,7 +31,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 event-driven-architecture diff --git a/execute-around/pom.xml b/execute-around/pom.xml index 23a0a4a15170..a2b68d656683 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 execute-around diff --git a/facade/pom.xml b/facade/pom.xml index a526243a1204..0fe220e2d200 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 facade diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml index ea2aa597625a..9f05e95fc525 100644 --- a/factory-kit/pom.xml +++ b/factory-kit/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 factory-kit diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 7e1cfd28f543..bf4fc1c37168 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 factory-method diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index cf479dd0ae46..6121609d6903 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index 2e4129b2d1f8..e8da43e0c464 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index bd454ca23460..02a1de27c4a5 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index a4221d7625fd..ceb6af4c1a8a 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index c8f136ca803c..4a2f8160e3b5 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index 5b6ce56b1f48..e2f8cd62ae5c 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 half-sync-half-async diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 54f81cf144e5..0b4ca86ee112 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 hexagonal diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index 6ef1e25bc43a..a9daf16736b0 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index a0003672a5e8..2017535f5a32 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index dcd70f809f22..d5bf2b037bba 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 iterator diff --git a/layers/pom.xml b/layers/pom.xml index abd888afaea6..d2bb976958fe 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index 37b321aecd85..c607ccf1a841 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index 7f3209479882..05a9159b4df5 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 mediator diff --git a/memento/pom.xml b/memento/pom.xml index b7355784c1d2..9379cc83b55f 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index 3afc5b32ea44..3b35367de761 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 0ae6a6fc614d..d3569e95159a 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index e0a3c57fbde3..5185ff8f9e1f 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 model-view-presenter model-view-presenter diff --git a/monad/pom.xml b/monad/pom.xml index b737e2174af5..6b2b03eabb6a 100644 --- a/monad/pom.xml +++ b/monad/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 monad diff --git a/monostate/pom.xml b/monostate/pom.xml index bb246d38c5e5..486f821ffab5 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 monostate diff --git a/multiton/pom.xml b/multiton/pom.xml index 2482c056bac8..b5205b629834 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 multiton diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml index 3bfc3bf8aed4..8508bebc370b 100644 --- a/mute-idiom/pom.xml +++ b/mute-idiom/pom.xml @@ -21,7 +21,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 mute-idiom diff --git a/mutex/pom.xml b/mutex/pom.xml index c9e0e83b7087..1801bce35ab4 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 mutex diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index be96b2456aee..81f53762828e 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0-SNAPSHOT + 1.12.0 naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index 6bbaaaeb16ef..cefed25f9003 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0-SNAPSHOT + 1.12.0 naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index 68cac3790dcc..283d7ad0de31 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0-SNAPSHOT + 1.12.0 naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index d34dba9a3953..2c0ac4d6ca73 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 naked-objects @@ -350,17 +350,17 @@ ${project.groupId} naked-objects-dom - 1.12.0-SNAPSHOT + 1.12.0 ${project.groupId} naked-objects-fixture - 1.12.0-SNAPSHOT + 1.12.0 ${project.groupId} naked-objects-webapp - 1.12.0-SNAPSHOT + 1.12.0 diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index aa7d60438520..8378d3c41e7b 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0-SNAPSHOT + 1.12.0 naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index b765c6537561..92ff7d0acb7f 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index 6022ef8a7485..be163b80ff77 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 object-pool diff --git a/observer/pom.xml b/observer/pom.xml index fcc3e88aa229..c50735fb19f5 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 observer diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index b76578122e2f..3e53dc3458a7 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 poison-pill diff --git a/pom.xml b/pom.xml index 9469941f51f1..4b0b355cb95b 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 pom 2014 diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 8771976647e1..01e0f5d3ec4f 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 private-class-data diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index c4ea8f1df60d..1c4231781956 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 producer-consumer diff --git a/property/pom.xml b/property/pom.xml index c753261d1ad0..371a6438c2c8 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 property diff --git a/prototype/pom.xml b/prototype/pom.xml index 7fb43402ff40..7dbf3e5c9934 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index 9936be34bc2c..60f9b0cfecf9 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 proxy diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml index 1ff97ca4703d..60f65e551d9a 100644 --- a/publish-subscribe/pom.xml +++ b/publish-subscribe/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 publish-subscribe diff --git a/reactor/pom.xml b/reactor/pom.xml index 80a15f9d39a2..0f9d59ba3057 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 reactor diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index 0391ea29164b..0216bf56f570 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 reader-writer-lock diff --git a/repository/pom.xml b/repository/pom.xml index b8b81ee05abd..4bc5394e1c9d 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index 63971d97c5f4..a020f13a68d3 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 resource-acquisition-is-initialization diff --git a/semaphore/pom.xml b/semaphore/pom.xml index a2dc143a1188..93d98d19d8ad 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 semaphore diff --git a/servant/pom.xml b/servant/pom.xml index a102ad348077..c3f56d520782 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index c5e4eb8d9691..56e9c8e69b9c 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index f07dc93f860a..ec85c456012b 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index 8ab7e0686196..175ae294b4e1 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 singleton diff --git a/specification/pom.xml b/specification/pom.xml index c5468891962c..9aa313141498 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 specification diff --git a/state/pom.xml b/state/pom.xml index 2411eb2a72fc..83fc72dc78e9 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 8d926d118d3c..31b11816ba77 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.12.0-SNAPSHOT + 1.12.0 step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index 934ff8775b41..b93a14eb1df2 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index af0b19cb419b..5f649271490f 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index 824dcd7af0da..69a99b94afbd 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index 249788da0ed1..2dafeb376f68 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 tolerant-reader diff --git a/twin/pom.xml b/twin/pom.xml index 010bf37b2e80..b0e280fe9c35 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 twin diff --git a/value-object/pom.xml b/value-object/pom.xml index 66b2342eb90b..d6866bebfe48 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 value-object diff --git a/visitor/pom.xml b/visitor/pom.xml index 1b594b1791c3..6797315ff019 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0-SNAPSHOT + 1.12.0 visitor From 5c26f4617464e5e93c48dcf39e7ebd7373b82e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 30 Jun 2016 18:15:26 +0300 Subject: [PATCH 100/207] Set version number for next development iteration --- abstract-document/pom.xml | 2 +- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- aggregator-microservices/aggregator-service/pom.xml | 2 +- aggregator-microservices/information-microservice/pom.xml | 2 +- aggregator-microservices/inventory-microservice/pom.xml | 2 +- aggregator-microservices/pom.xml | 2 +- api-gateway/api-gateway-service/pom.xml | 2 +- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- caching/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- data-mapper/pom.xml | 2 +- decorator/pom.xml | 2 +- delegation/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- event-driven-architecture/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-kit/pom.xml | 2 +- factory-method/pom.xml | 2 +- feature-toggle/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- hexagonal/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- monad/pom.xml | 2 +- monostate/pom.xml | 2 +- multiton/pom.xml | 2 +- mute-idiom/pom.xml | 2 +- mutex/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- producer-consumer/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- publish-subscribe/pom.xml | 2 +- reactor/pom.xml | 2 +- reader-writer-lock/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- semaphore/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- twin/pom.xml | 2 +- value-object/pom.xml | 2 +- visitor/pom.xml | 2 +- 90 files changed, 93 insertions(+), 93 deletions(-) diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index 36f014e67f1c..b7a348d26617 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT abstract-document diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index 668ebbbd1317..ec0f700a53cc 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index f8e550acd919..2c99796f4098 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT adapter diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index 8b8156e1b3a6..169d0da94ff3 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index 1c806e1fe3a9..9865403446f3 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index b8420895261d..f8844dd39774 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index bf20ece3381c..0133e9ea486d 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 aggregator-microservices diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index a99214ab30cf..87a1ebb5ef96 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 api-gateway-service diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index 414a3d97e478..c29932fae1e2 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 870980cad77b..471ffda7db34 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index db9061126c2e..24e9663aa7c7 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index 7bab122e3d8e..d912df965908 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index 740fa659a860..a7a3883c8cb8 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT bridge diff --git a/builder/pom.xml b/builder/pom.xml index 676d2f4299c7..6c171bef4e9d 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 70f7a8889198..c6f7e0c3715d 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT business-delegate diff --git a/caching/pom.xml b/caching/pom.xml index 9e1e95c9675b..c20842a892a2 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT caching diff --git a/callback/pom.xml b/callback/pom.xml index 594d91b52019..4fe17d1431cb 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT callback diff --git a/chain/pom.xml b/chain/pom.xml index 202e29eba20d..ee3b92401bb1 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT chain diff --git a/command/pom.xml b/command/pom.xml index fca18c3fd779..a27a56e54809 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT command diff --git a/composite/pom.xml b/composite/pom.xml index 6dfb47d38f67..ace29d8d13b7 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT composite diff --git a/dao/pom.xml b/dao/pom.xml index 1b56f0ed041f..f64eff8bc251 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT dao diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 8e2845fd7f23..7d1b8346991a 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT data-mapper diff --git a/decorator/pom.xml b/decorator/pom.xml index adda649a88fc..7ba2a4ee8029 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT decorator diff --git a/delegation/pom.xml b/delegation/pom.xml index a2c9c82d241e..2fca225b83ea 100644 --- a/delegation/pom.xml +++ b/delegation/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index b5727803d1c1..3472da2400ae 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index c98e82ec3a48..239bcf87098f 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -27,7 +27,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 77a456bbe521..4f31b2e7e51d 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 5517934bb84c..b7de6e01b1b5 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT event-aggregator diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml index 910654fc6bc8..99ed39891ffd 100644 --- a/event-driven-architecture/pom.xml +++ b/event-driven-architecture/pom.xml @@ -31,7 +31,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT event-driven-architecture diff --git a/execute-around/pom.xml b/execute-around/pom.xml index a2b68d656683..60bb6d0ab912 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT execute-around diff --git a/facade/pom.xml b/facade/pom.xml index 0fe220e2d200..e0e0f4a4d388 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT facade diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml index 9f05e95fc525..6c936de5c04b 100644 --- a/factory-kit/pom.xml +++ b/factory-kit/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT factory-kit diff --git a/factory-method/pom.xml b/factory-method/pom.xml index bf4fc1c37168..5081ecf9960e 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT factory-method diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 6121609d6903..78c182af9d03 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index e8da43e0c464..ca5e115d09a3 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index 02a1de27c4a5..c07cca157ed1 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index ceb6af4c1a8a..959fa5548103 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index 4a2f8160e3b5..2a448cffea85 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index e2f8cd62ae5c..076dfb44daa9 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT half-sync-half-async diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 0b4ca86ee112..e9e2a502d54c 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT hexagonal diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index a9daf16736b0..6bb6f95cc5e1 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 2017535f5a32..9f5dd31f30c7 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index d5bf2b037bba..484030a33fb9 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT iterator diff --git a/layers/pom.xml b/layers/pom.xml index d2bb976958fe..bf155b655f69 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index c607ccf1a841..fe6d44c78d90 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index 05a9159b4df5..a6cdd028eea6 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT mediator diff --git a/memento/pom.xml b/memento/pom.xml index 9379cc83b55f..a320f186cb40 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index 3b35367de761..cbeba7a75c98 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index d3569e95159a..3c01f6e8dd22 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 5185ff8f9e1f..0aad02b27772 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT model-view-presenter model-view-presenter diff --git a/monad/pom.xml b/monad/pom.xml index 6b2b03eabb6a..7f128272d5db 100644 --- a/monad/pom.xml +++ b/monad/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT monad diff --git a/monostate/pom.xml b/monostate/pom.xml index 486f821ffab5..64ee52abe3cd 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT monostate diff --git a/multiton/pom.xml b/multiton/pom.xml index b5205b629834..0b835ed4d5c0 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT multiton diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml index 8508bebc370b..c138c5ed4fea 100644 --- a/mute-idiom/pom.xml +++ b/mute-idiom/pom.xml @@ -21,7 +21,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT mute-idiom diff --git a/mutex/pom.xml b/mutex/pom.xml index 1801bce35ab4..852006cd46cc 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT mutex diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index 81f53762828e..1194166941d8 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0 + 1.13.0-SNAPSHOT naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index cefed25f9003..46b28146895f 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0 + 1.13.0-SNAPSHOT naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index 283d7ad0de31..dccaf64a3f16 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0 + 1.13.0-SNAPSHOT naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index 2c0ac4d6ca73..b3e48dcb69bc 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT naked-objects @@ -350,17 +350,17 @@ ${project.groupId} naked-objects-dom - 1.12.0 + 1.13.0-SNAPSHOT ${project.groupId} naked-objects-fixture - 1.12.0 + 1.13.0-SNAPSHOT ${project.groupId} naked-objects-webapp - 1.12.0 + 1.13.0-SNAPSHOT diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index 8378d3c41e7b..762438aca09f 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.12.0 + 1.13.0-SNAPSHOT naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index 92ff7d0acb7f..b0adad1af47f 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index be163b80ff77..666242d1d34b 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT object-pool diff --git a/observer/pom.xml b/observer/pom.xml index c50735fb19f5..847a7ea89539 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT observer diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 3e53dc3458a7..9717f5e13cda 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT poison-pill diff --git a/pom.xml b/pom.xml index 4b0b355cb95b..939b85c0f9fd 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT pom 2014 diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 01e0f5d3ec4f..20484245bdd5 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT private-class-data diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index 1c4231781956..a72aa2c960e7 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT producer-consumer diff --git a/property/pom.xml b/property/pom.xml index 371a6438c2c8..1e9d0f0c2b16 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT property diff --git a/prototype/pom.xml b/prototype/pom.xml index 7dbf3e5c9934..e8bb4303a9ef 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index 60f9b0cfecf9..f16736a2c27d 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT proxy diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml index 60f65e551d9a..6db6b35384b5 100644 --- a/publish-subscribe/pom.xml +++ b/publish-subscribe/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT publish-subscribe diff --git a/reactor/pom.xml b/reactor/pom.xml index 0f9d59ba3057..c06584c6f05c 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT reactor diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index 0216bf56f570..2df3d5b147a9 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT reader-writer-lock diff --git a/repository/pom.xml b/repository/pom.xml index 4bc5394e1c9d..56a448ec57c8 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index a020f13a68d3..7b411f46c790 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT resource-acquisition-is-initialization diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 93d98d19d8ad..1b3bf8b5d853 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT semaphore diff --git a/servant/pom.xml b/servant/pom.xml index c3f56d520782..c235b005cd32 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index 56e9c8e69b9c..23d78859591b 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index ec85c456012b..0f17b3dcc19a 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index 175ae294b4e1..ab9405c5e750 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT singleton diff --git a/specification/pom.xml b/specification/pom.xml index 9aa313141498..03c66540d54c 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT specification diff --git a/state/pom.xml b/state/pom.xml index 83fc72dc78e9..134fbabe180f 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 31b11816ba77..96098eabca56 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.12.0 + 1.13.0-SNAPSHOT step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index b93a14eb1df2..e6fa58081df2 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index 5f649271490f..ee533df8f44b 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index 69a99b94afbd..e7fa43103ef2 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index 2dafeb376f68..e5dd3ba8874c 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT tolerant-reader diff --git a/twin/pom.xml b/twin/pom.xml index b0e280fe9c35..1eb854b87384 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT twin diff --git a/value-object/pom.xml b/value-object/pom.xml index d6866bebfe48..ec8de16811b7 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT value-object diff --git a/visitor/pom.xml b/visitor/pom.xml index 6797315ff019..54a90e1840f4 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.12.0 + 1.13.0-SNAPSHOT visitor From b1e40d9c925800b06c535e92f8f1c13ebb23d7a3 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sun, 3 Jul 2016 21:12:09 +0100 Subject: [PATCH 101/207] Feedback changes - final and refactored code --- .../pageobject/AlbumListPageTest.java | 12 +++-- .../iluwatar/pageobject/AlbumPageTest.java | 10 ++++- .../iluwatar/pageobject/LoginPageTest.java | 15 +++++-- .../pageobject/pages/AlbumListPage.java | 22 +++++----- .../iluwatar/pageobject/pages/AlbumPage.java | 44 +++++++------------ .../iluwatar/pageobject/pages/LoginPage.java | 27 +++++------- .../com/iluwatar/pageobject/pages/Page.java | 2 +- 7 files changed, 71 insertions(+), 61 deletions(-) diff --git a/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java index 5afc05f5593d..b97410036005 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java @@ -26,6 +26,7 @@ import com.gargoylesoftware.htmlunit.WebClient; import com.iluwatar.pageobject.pages.AlbumListPage; import com.iluwatar.pageobject.pages.AlbumPage; +import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertTrue; @@ -33,12 +34,17 @@ public class AlbumListPageTest { + private AlbumListPage albumListPage = new AlbumListPage(new WebClient()); + + @Before + public void setUp() { + albumListPage.navigateToPage(); + } + @Test public void testSelectAlbum() { - AlbumListPage albumListPage = new AlbumListPage(new WebClient()); - AlbumPage albumPage = albumListPage.selectAlbum("21"); - + albumPage.navigateToPage(); assertTrue(albumPage.isAt()); } diff --git a/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java index 811ffd02a2ca..01461cc8da41 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java @@ -25,6 +25,7 @@ import com.gargoylesoftware.htmlunit.WebClient; import com.iluwatar.pageobject.pages.AlbumListPage; import com.iluwatar.pageobject.pages.AlbumPage; +import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertTrue; @@ -33,10 +34,16 @@ public class AlbumPageTest { private AlbumPage albumPage = new AlbumPage(new WebClient()); + @Before + public void setUp() { + albumPage.navigateToPage(); + } + @Test public void testSaveAlbum() { - AlbumPage albumPageAfterChanges = albumPage.changeAlbumTitle("25") + AlbumPage albumPageAfterChanges = albumPage + .changeAlbumTitle("25") .changeArtist("Adele Laurie Blue Adkins") .changeAlbumYear(2015) .changeAlbumRating("B") @@ -50,6 +57,7 @@ public void testSaveAlbum() { @Test public void testCancelChanges() { AlbumListPage albumListPage = albumPage.cancelChanges(); + albumListPage.navigateToPage(); assertTrue(albumListPage.isAt()); } diff --git a/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java index b9e8e782df3b..659df062933e 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java @@ -25,20 +25,27 @@ import com.gargoylesoftware.htmlunit.WebClient; import com.iluwatar.pageobject.pages.AlbumListPage; import com.iluwatar.pageobject.pages.LoginPage; +import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertTrue; public class LoginPageTest { + private LoginPage loginPage = new LoginPage(new WebClient()); + + @Before + public void setUp() { + loginPage.navigateToPage(); + } + @Test public void testLogin() { - LoginPage loginPage = new LoginPage(new WebClient()); - - AlbumListPage albumListPage = loginPage.enterUsername("admin") + AlbumListPage albumListPage = loginPage + .enterUsername("admin") .enterPassword("password") .login(); - + albumListPage.navigateToPage(); assertTrue(albumListPage.isAt()); } diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java index 7f4e50fb5834..fb322ff53d0a 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java @@ -27,7 +27,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import java.io.IOException; -import java.net.MalformedURLException; import java.util.List; /** @@ -40,26 +39,27 @@ public class AlbumListPage extends Page { private HtmlPage page; - private List albumLinks; - /** * Constructor */ public AlbumListPage(WebClient webClient) { super(webClient); - try { - page = this.webClient.getPage(PAGE_URL); + } - // uses XPath to find list of html anchor tags with the class album in it - albumLinks = (List) page.getByXPath("//tr[@class='album']//a"); - } catch (MalformedURLException e) { - e.printStackTrace(); + /** + * Navigates to the Album List Page + * + * @return {@link AlbumListPage} + */ + public AlbumListPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); } catch (IOException e) { e.printStackTrace(); } - + return this; } /** @@ -77,6 +77,8 @@ public boolean isAt() { * @return the album page */ public AlbumPage selectAlbum(String albumTitle) { + // uses XPath to find list of html anchor tags with the class album in it + List albumLinks = (List) page.getByXPath("//tr[@class='album']//a"); for (HtmlAnchor anchor : albumLinks) { if (anchor.getTextContent().equals(albumTitle)) { try { diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java index b2df6ef33e00..4dffb93dd997 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java @@ -31,7 +31,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlTextInput; import java.io.IOException; -import java.net.MalformedURLException; /** * Page Object encapsulating the Album Page (album-page.html) @@ -43,43 +42,27 @@ public class AlbumPage extends Page { private HtmlPage page; - private HtmlTextInput albumTitleInputTextField; - private HtmlTextInput artistInputTextField; - private HtmlSelect albumYearSelectOption; - private HtmlTextInput albumRatingInputTextField; - private HtmlNumberInput numberOfSongsNumberField; - - private HtmlSubmitInput cancelButton; - private HtmlSubmitInput saveButton; /** * Constructor */ public AlbumPage(WebClient webClient) { super(webClient); + } + + + /** + * Navigates to the album page + * + * @return {@link AlbumPage} + */ + public AlbumPage navigateToPage() { try { page = this.webClient.getPage(PAGE_URL); - initializeHtmlElements(); - } catch (MalformedURLException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); } - - } - - - private void initializeHtmlElements() { - albumTitleInputTextField = (HtmlTextInput) page.getElementById("albumTitle"); - artistInputTextField = (HtmlTextInput) page.getElementById("albumArtist"); - albumYearSelectOption = (HtmlSelect) page.getElementById("albumYear"); - albumRatingInputTextField = (HtmlTextInput) page.getElementById("albumRating"); - numberOfSongsNumberField = (HtmlNumberInput) page.getElementById("numberOfSongs"); - - cancelButton = (HtmlSubmitInput) page.getElementById("cancelButton"); - saveButton = (HtmlSubmitInput) page.getElementById("saveButton"); + return this; } @@ -99,6 +82,7 @@ public boolean isAt() { * @return {@link AlbumPage} */ public AlbumPage changeAlbumTitle(String albumTitle) { + HtmlTextInput albumTitleInputTextField = (HtmlTextInput) page.getElementById("albumTitle"); albumTitleInputTextField.setText(albumTitle); return this; } @@ -111,6 +95,7 @@ public AlbumPage changeAlbumTitle(String albumTitle) { * @return {@link AlbumPage} */ public AlbumPage changeArtist(String artist) { + HtmlTextInput artistInputTextField = (HtmlTextInput) page.getElementById("albumArtist"); artistInputTextField.setText(artist); return this; } @@ -123,6 +108,7 @@ public AlbumPage changeArtist(String artist) { * @return {@link AlbumPage} */ public AlbumPage changeAlbumYear(int year) { + HtmlSelect albumYearSelectOption = (HtmlSelect) page.getElementById("albumYear"); HtmlOption yearOption = albumYearSelectOption.getOptionByValue(Integer.toString(year)); albumYearSelectOption.setSelectedAttribute(yearOption, true); return this; @@ -136,6 +122,7 @@ public AlbumPage changeAlbumYear(int year) { * @return {@link AlbumPage} */ public AlbumPage changeAlbumRating(String albumRating) { + HtmlTextInput albumRatingInputTextField = (HtmlTextInput) page.getElementById("albumRating"); albumRatingInputTextField.setText(albumRating); return this; } @@ -147,6 +134,7 @@ public AlbumPage changeAlbumRating(String albumRating) { * @return {@link AlbumPage} */ public AlbumPage changeNumberOfSongs(int numberOfSongs) { + HtmlNumberInput numberOfSongsNumberField = (HtmlNumberInput) page.getElementById("numberOfSongs"); numberOfSongsNumberField.setText(Integer.toString(numberOfSongs)); return this; } @@ -158,6 +146,7 @@ public AlbumPage changeNumberOfSongs(int numberOfSongs) { * @return {@link AlbumListPage} */ public AlbumListPage cancelChanges() { + HtmlSubmitInput cancelButton = (HtmlSubmitInput) page.getElementById("cancelButton"); try { cancelButton.click(); } catch (IOException e) { @@ -173,6 +162,7 @@ public AlbumListPage cancelChanges() { * @return {@link AlbumPage} */ public AlbumPage saveChanges() { + HtmlSubmitInput saveButton = (HtmlSubmitInput) page.getElementById("saveButton"); try { saveButton.click(); } catch (IOException e) { diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java index ac12f720f394..d4bd0850ced1 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java @@ -28,7 +28,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import com.gargoylesoftware.htmlunit.html.HtmlTextInput; import java.io.IOException; -import java.net.MalformedURLException; /** * Page Object encapsulating the Login Page (login.html) @@ -40,11 +39,6 @@ public class LoginPage extends Page { private HtmlPage page; - private HtmlTextInput usernameInputTextField; - private HtmlPasswordInput passwordInputPasswordField; - private HtmlSubmitInput loginButton; - - /** * Constructor * @@ -52,21 +46,22 @@ public class LoginPage extends Page { */ public LoginPage(WebClient webClient) { super(webClient); + } + + /** + * Navigates to the Login page + * + * @return {@link LoginPage} + */ + public LoginPage navigateToPage() { try { page = this.webClient.getPage(PAGE_URL); - - usernameInputTextField = (HtmlTextInput) page.getElementById("username"); - passwordInputPasswordField = (HtmlPasswordInput) page.getElementById("password"); - loginButton = (HtmlSubmitInput) page.getElementById("loginButton"); - - } catch (MalformedURLException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } + return this; } - /** * {@inheritDoc} */ @@ -83,6 +78,7 @@ public boolean isAt() { * @return {@link LoginPage} */ public LoginPage enterUsername(String username) { + HtmlTextInput usernameInputTextField = (HtmlTextInput) page.getElementById("username"); usernameInputTextField.setText(username); return this; } @@ -95,6 +91,7 @@ public LoginPage enterUsername(String username) { * @return {@link LoginPage} */ public LoginPage enterPassword(String password) { + HtmlPasswordInput passwordInputPasswordField = (HtmlPasswordInput) page.getElementById("password"); passwordInputPasswordField.setText(password); return this; } @@ -107,6 +104,7 @@ public LoginPage enterPassword(String password) { * - this is the page that user gets navigated to once successfully logged in */ public AlbumListPage login() { + HtmlSubmitInput loginButton = (HtmlSubmitInput) page.getElementById("loginButton"); try { loginButton.click(); } catch (IOException e) { @@ -115,5 +113,4 @@ public AlbumListPage login() { return new AlbumListPage(webClient); } - } diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java index 340a728006ac..b0b328e7c378 100644 --- a/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java @@ -35,7 +35,7 @@ public abstract class Page { */ public static final String AUT_PATH = "src/main/resources/sample-ui/"; - protected WebClient webClient; + protected final WebClient webClient; /** * Constructor From 88321890c78ccf710eca84a648beb7a33d86f424 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sun, 3 Jul 2016 21:12:48 +0100 Subject: [PATCH 102/207] remove old class diagram --- page-object/etc/page-object.png | Bin 78043 -> 0 bytes page-object/etc/page-object.ucls | 60 ------------------------------- 2 files changed, 60 deletions(-) delete mode 100644 page-object/etc/page-object.png delete mode 100644 page-object/etc/page-object.ucls diff --git a/page-object/etc/page-object.png b/page-object/etc/page-object.png deleted file mode 100644 index 27b99b4490631740c2fe68c119290435c6db08f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78043 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 + + + + + + + \ No newline at end of file From ef2ada4fdf87d6500eb348d3bd3424bce5233784 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sun, 3 Jul 2016 21:47:07 +0100 Subject: [PATCH 104/207] syncing fork --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 0b797b9b8bfa..73aa7bcfd49e 100644 --- a/pom.xml +++ b/pom.xml @@ -229,6 +229,12 @@ ${hierarchical-junit-runner-version} test + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + From f806c5b71224b03e342370b5ff2de618e0af2ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Mon, 4 Jul 2016 21:52:39 +0300 Subject: [PATCH 105/207] Add known uses to Factory Method --- factory-method/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/factory-method/README.md b/factory-method/README.md index 42237883d6b6..09d7b481d158 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -27,6 +27,12 @@ Use the Factory Method pattern when * a class wants its subclasses to specify the objects it creates * classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate +## Known uses + +java.util.Calendar +java.util.ResourceBundle +java.text.NumberFormat#getInstance() + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) From 849df503940caf4e922e0f7e2db1788786d3382a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Mon, 4 Jul 2016 22:22:37 +0300 Subject: [PATCH 106/207] Fix formatting --- factory-method/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/factory-method/README.md b/factory-method/README.md index 09d7b481d158..05549cf4f270 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -29,9 +29,9 @@ Use the Factory Method pattern when ## Known uses -java.util.Calendar -java.util.ResourceBundle -java.text.NumberFormat#getInstance() +* java.util.Calendar +* java.util.ResourceBundle +* java.text.NumberFormat#getInstance() ## Credits From ff8037ee0b3b6e44f86ad6221523784343c38ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 7 Jul 2016 08:46:56 +0300 Subject: [PATCH 107/207] Add aliases for Specification pattern --- specification/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specification/README.md b/specification/README.md index df6a4c3ebd31..5f9aa4a36701 100644 --- a/specification/README.md +++ b/specification/README.md @@ -9,6 +9,9 @@ tags: - Difficulty-Beginner --- +## Also known as +Filter, Criteria + ## Intent Specification pattern separates the statement of how to match a candidate, from the candidate object that it is matched against. As well as its From 7be98fa829efd68c792197e33930c6862023fe47 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sat, 9 Jul 2016 22:20:13 +0100 Subject: [PATCH 108/207] added missing licence headers to 2 files --- .../resources/sample-ui/css/album-list.css | 24 +++++++++++++++++++ .../main/resources/sample-ui/css/style.css | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/page-object/src/main/resources/sample-ui/css/album-list.css b/page-object/src/main/resources/sample-ui/css/album-list.css index 9c828e1a0681..f7a077a70963 100644 --- a/page-object/src/main/resources/sample-ui/css/album-list.css +++ b/page-object/src/main/resources/sample-ui/css/album-list.css @@ -1,3 +1,27 @@ + table { font-size: 16px; border-collapse: collapse; diff --git a/page-object/src/main/resources/sample-ui/css/style.css b/page-object/src/main/resources/sample-ui/css/style.css index 6efb32df9a1f..500180d4de84 100644 --- a/page-object/src/main/resources/sample-ui/css/style.css +++ b/page-object/src/main/resources/sample-ui/css/style.css @@ -1,3 +1,27 @@ + body { font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; } \ No newline at end of file From 85f432fb545f25bd96a89b34c1cdf8cf19f4879c Mon Sep 17 00:00:00 2001 From: Colin But Date: Sat, 9 Jul 2016 22:24:17 +0100 Subject: [PATCH 109/207] exclude library class from jacoco code coverage --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 73aa7bcfd49e..6f3e0d69896f 100644 --- a/pom.xml +++ b/pom.xml @@ -307,6 +307,7 @@ domainapp/dom/modules/simple/QSimpleObject.class + **com.steadystate* From f182e87ee175dd7aa970a6ec659c9ef3eebfe6fb Mon Sep 17 00:00:00 2001 From: Colin But Date: Sun, 10 Jul 2016 20:51:52 +0100 Subject: [PATCH 110/207] fixed app launch --- .../java/com/iluwatar/pageobject/App.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/page-object/src/main/java/com/iluwatar/pageobject/App.java b/page-object/src/main/java/com/iluwatar/pageobject/App.java index 67b3f947b653..235eedd25528 100644 --- a/page-object/src/main/java/com/iluwatar/pageobject/App.java +++ b/page-object/src/main/java/com/iluwatar/pageobject/App.java @@ -69,25 +69,21 @@ private App() { */ public static void main(String[] args) { - String currentWorkingDir = System.getProperty("user.dir"); - File applicationFile = new File(currentWorkingDir - + "/page-object/src/main/resources/sample-ui/login.html"); + try { + File applicationFile = new File(App.class.getClassLoader().getResource("sample-ui/login.html").getPath()); - // should work for unix like OS (mac, unix etc...) - if (Desktop.isDesktopSupported()) { - try { + // should work for unix like OS (mac, unix etc...) + if (Desktop.isDesktopSupported()) { Desktop.getDesktop().open(applicationFile); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - // java Desktop not supported - above unlikely to work for Windows so try following instead... - try { + + } else { + // java Desktop not supported - above unlikely to work for Windows so try following instead... Runtime.getRuntime().exec("cmd.exe start " + applicationFile); - } catch (IOException e) { - e.printStackTrace(); } + } catch (IOException ex) { + ex.printStackTrace(); } + } } From fe1d760c7f66338834443bf84d3efbe28a3b379b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 12 Jul 2016 19:52:56 +0300 Subject: [PATCH 111/207] Fix license headers --- .../resources/sample-ui/css/album-list.css | 22 +++++++++++++++++++ .../main/resources/sample-ui/css/style.css | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/page-object/src/main/resources/sample-ui/css/album-list.css b/page-object/src/main/resources/sample-ui/css/album-list.css index f7a077a70963..61fd0af813cc 100644 --- a/page-object/src/main/resources/sample-ui/css/album-list.css +++ b/page-object/src/main/resources/sample-ui/css/album-list.css @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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. + */ + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + promise + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java new file mode 100644 index 000000000000..dc22c307a7dc --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -0,0 +1,24 @@ +package com.iluwatar.promise; + +public class App { + + /** + * Program entry point + * @param args arguments + */ + public static void main(String[] args) { + ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + executor.execute(() -> { + Thread.sleep(1000); + return 10; + }).then(value -> {System.out.println("Consumed the value: " + value);}) + .then(nullVal -> {System.out.println("Post consuming value");}); + + + executor.execute(() -> { + Thread.sleep(1000); + return "10"; + }).then(value -> {return 10 + Integer.parseInt(value);}) + .then(intValue -> {System.out.println("Consumed int value: " + intValue);}); + } +} diff --git a/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java b/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java new file mode 100644 index 000000000000..a68154e17923 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java @@ -0,0 +1,12 @@ +package com.iluwatar.promise; + +import java.util.function.Consumer; +import java.util.function.Function; + +import com.iluwatar.async.method.invocation.AsyncResult; + +public interface ListenableAsyncResult extends AsyncResult { + ListenableAsyncResult then(Consumer action); + ListenableAsyncResult then(Function func); + ListenableAsyncResult error(Consumer action); +} diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java new file mode 100644 index 000000000000..eb43b05469c3 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java @@ -0,0 +1,7 @@ +package com.iluwatar.promise; + +import java.util.concurrent.Callable; + +public interface PromiseAsyncExecutor { + ListenableAsyncResult execute(Callable task); +} diff --git a/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java new file mode 100644 index 000000000000..aa057d676264 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java @@ -0,0 +1,173 @@ +package com.iluwatar.promise; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; + +public class ThreadAsyncExecutor implements PromiseAsyncExecutor { + + /** Index for thread naming */ + private final AtomicInteger idx = new AtomicInteger(0); + + @Override + public ListenableAsyncResult execute(Callable task) { + Promise promise = new Promise<>(); + new Thread(() -> { + try { + promise.setValue(task.call()); + promise.postComplete(); + } catch (Exception ex) { + promise.setException(ex); + } + } , "executor-" + idx.incrementAndGet()).start(); + return promise; + } + + // TODO there is scope of extending the completable future from async method invocation project. Do that. + private class Promise implements ListenableAsyncResult { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + volatile int state = RUNNING; + T value; + Exception exception; + Runnable fulfilmentAction; + + public Promise() { + this.lock = new Object(); + } + + void postComplete() { + fulfilmentAction.run(); + } + + /** + * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param value + * value of the evaluated task + */ + public void setValue(T value) { + this.value = value; + this.state = COMPLETED; + synchronized (lock) { + lock.notifyAll(); + } + } + + /** + * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param exception + * exception of the failed task + */ + public void setException(Exception exception) { + this.exception = exception; + this.state = FAILED; + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean isCompleted() { + return state > RUNNING; + } + + @Override + public T getValue() throws ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new IllegalStateException("Execution not completed yet"); + } + } + + @Override + public void await() throws InterruptedException { + synchronized (lock) { + if (!isCompleted()) { + lock.wait(); + } + } + } + + @Override + public ListenableAsyncResult then(Consumer action) { + Promise dest = new Promise<>(); + fulfilmentAction = new ConsumeAction(this, dest, action); + return dest; + } + + @Override + public ListenableAsyncResult then(Function func) { + Promise dest = new Promise<>(); + fulfilmentAction = new FunctionAction(this, dest, func); + return dest; + } + + @Override + public ListenableAsyncResult error(Consumer action) { + return null; + } + + private class ConsumeAction implements Runnable { + + private Promise current; + private Promise dest; + private Consumer action; + + public ConsumeAction(Promise current, Promise dest, Consumer action) { + this.current = current; + this.dest = dest; + this.action = action; + } + + @Override + public void run() { + try { + action.accept(current.getValue()); + dest.setValue(null); + } catch (ExecutionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + dest.postComplete(); + } + } + + private class FunctionAction implements Runnable { + + private Promise current; + private Promise dest; + private Function func; + + public FunctionAction(Promise current, Promise dest, Function func) { + this.current = current; + this.dest = dest; + this.func = func; + } + + @Override + public void run() { + try { + V result = func.apply(current.getValue()); + dest.setValue(result); + } catch (ExecutionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + dest.postComplete(); + } + } + } + } \ No newline at end of file From 8124fd07717a67585533fcbcea6afea855836c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 21 Jul 2016 09:27:48 +0300 Subject: [PATCH 116/207] Adjust pattern categories and tags --- async-method-invocation/README.md | 1 + command/README.md | 1 + event-aggregator/README.md | 1 + event-driven-architecture/README.md | 7 +++++++ monad/README.md | 2 +- mute-idiom/README.md | 4 ++-- mutex/README.md | 4 ++-- observer/README.md | 1 + page-object/README.md | 3 --- poison-pill/README.md | 1 + producer-consumer/README.md | 1 + reader-writer-lock/README.md | 7 +++++-- semaphore/README.md | 4 ++-- specification/README.md | 4 ++++ 14 files changed, 29 insertions(+), 12 deletions(-) diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 93c0249d9639..2d99820c5d82 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -8,6 +8,7 @@ tags: - Java - Difficulty-Intermediate - Functional + - Reactive --- ## Intent diff --git a/command/README.md b/command/README.md index ee0003ab780c..a5478394cda1 100644 --- a/command/README.md +++ b/command/README.md @@ -8,6 +8,7 @@ tags: - Java - Gang Of Four - Difficulty-Intermediate + - Functional --- ## Also known as diff --git a/event-aggregator/README.md b/event-aggregator/README.md index 5462a2a5d8bc..ac07869e7a89 100644 --- a/event-aggregator/README.md +++ b/event-aggregator/README.md @@ -7,6 +7,7 @@ categories: Structural tags: - Java - Difficulty-Beginner + - Reactive --- ## Intent diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md index 7c786b76c75c..c779fd430303 100644 --- a/event-driven-architecture/README.md +++ b/event-driven-architecture/README.md @@ -1,7 +1,14 @@ +--- layout: pattern title: Event Driven Architecture folder: event-driven-architecture permalink: /patterns/event-driven-architecture +categories: Architectural +tags: + - Java + - Difficulty-Intermediate + - Reactive +--- ## Intent Send and notify state changes of your objects to other applications using an Event-driven Architecture. diff --git a/monad/README.md b/monad/README.md index ffc67a3541b2..41edd3d92b4f 100644 --- a/monad/README.md +++ b/monad/README.md @@ -6,7 +6,7 @@ permalink: /patterns/monad/ categories: Other tags: - Java - - Difficulty-Advanced + - Difficulty-Expert - Functional --- diff --git a/mute-idiom/README.md b/mute-idiom/README.md index efc09f3069d0..bb674b648b48 100644 --- a/mute-idiom/README.md +++ b/mute-idiom/README.md @@ -11,7 +11,7 @@ tags: --- ## Intent -Provide a template to supress any exceptions that either are declared but cannot occur or should only be logged; +Provide a template to suppress any exceptions that either are declared but cannot occur or should only be logged; while executing some business logic. The template removes the need to write repeated `try-catch` blocks. @@ -20,7 +20,7 @@ while executing some business logic. The template removes the need to write repe ## Applicability Use this idiom when -* an API declares some exception but can never throw that exception. Eg. ByteArrayOutputStream bulk write method. +* an API declares some exception but can never throw that exception eg. ByteArrayOutputStream bulk write method. * you need to suppress some exception just by logging it, such as closing a resource. ## Credits diff --git a/mutex/README.md b/mutex/README.md index 24edad7eb76a..84755872f56f 100644 --- a/mutex/README.md +++ b/mutex/README.md @@ -3,10 +3,10 @@ layout: pattern title: Mutex folder: mutex permalink: /patterns/mutex/ -categories: Lock +categories: Concurrency tags: - Java - - Difficulty-Beginner + - Difficulty-Intermediate --- ## Also known as diff --git a/observer/README.md b/observer/README.md index db6320a1da52..6fbe3cdab9fd 100644 --- a/observer/README.md +++ b/observer/README.md @@ -8,6 +8,7 @@ tags: - Java - Difficulty-Beginner - Gang Of Four + - Reactive --- ## Also known as diff --git a/page-object/README.md b/page-object/README.md index ee1f827da0ee..b4f8246f117f 100644 --- a/page-object/README.md +++ b/page-object/README.md @@ -7,9 +7,6 @@ categories: Testing tags: - Java - Difficulty-Intermediate -- Testing -- Web Development -- Encapsulation --- ## Intent diff --git a/poison-pill/README.md b/poison-pill/README.md index eb37c5ada9f1..0815b376edf8 100644 --- a/poison-pill/README.md +++ b/poison-pill/README.md @@ -7,6 +7,7 @@ categories: Other tags: - Java - Difficulty-Intermediate + - Reactive --- ## Intent diff --git a/producer-consumer/README.md b/producer-consumer/README.md index c666ab12e2c4..1bb84c35fbb2 100644 --- a/producer-consumer/README.md +++ b/producer-consumer/README.md @@ -8,6 +8,7 @@ tags: - Java - Difficulty-Intermediate - I/O + - Reactive --- ## Intent diff --git a/reader-writer-lock/README.md b/reader-writer-lock/README.md index 91d16892c4a5..40b71136167b 100644 --- a/reader-writer-lock/README.md +++ b/reader-writer-lock/README.md @@ -3,9 +3,12 @@ layout: pattern title: Reader Writer Lock folder: reader-writer-lock permalink: /patterns/reader-writer-lock/ -categories: Concurrent +categories: Concurrency tags: -- Java + - Java + - Difficulty-Intermediate + - I/O + - Performance --- **Intent:** diff --git a/semaphore/README.md b/semaphore/README.md index e3e215d5aebd..46ccd7b8ea22 100644 --- a/semaphore/README.md +++ b/semaphore/README.md @@ -3,10 +3,10 @@ layout: pattern title: Semaphore folder: semaphore permalink: /patterns/semaphore/ -categories: Lock +categories: Concurrency tags: - Java - - Difficulty-Beginner + - Difficulty-Intermediate --- ## Also known as diff --git a/specification/README.md b/specification/README.md index 5f9aa4a36701..564830653d7f 100644 --- a/specification/README.md +++ b/specification/README.md @@ -26,6 +26,10 @@ Use the Specification pattern when * you need to select a subset of objects based on some criteria, and to refresh the selection at various times * you need to check that only suitable objects are used for a certain role (validation) +## Related patterns + +* Repository + ## Credits * [Martin Fowler - Specifications](http://martinfowler.com/apsupp/spec.pdf) From 102341443d33b7cca89f4e37b7c2e490fe780584 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 21 Jul 2016 19:13:42 +0530 Subject: [PATCH 117/207] Work on #403, added application class and test cases. --- pom.xml | 1 + promise/pom.xml | 5 + .../main/java/com/iluwatar/promise/App.java | 30 ++- .../promise/ListenableAsyncResult.java | 12 -- .../java/com/iluwatar/promise/Promise.java | 143 +++++++++++++++ .../promise/PromiseAsyncExecutor.java | 7 - .../iluwatar/promise/ThreadAsyncExecutor.java | 173 ------------------ .../java/com/iluwatar/promise/AppTest.java | 15 ++ .../com/iluwatar/promise/PromiseTest.java | 128 +++++++++++++ 9 files changed, 314 insertions(+), 200 deletions(-) delete mode 100644 promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java create mode 100644 promise/src/main/java/com/iluwatar/promise/Promise.java delete mode 100644 promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java delete mode 100644 promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java create mode 100644 promise/src/test/java/com/iluwatar/promise/AppTest.java create mode 100644 promise/src/test/java/com/iluwatar/promise/PromiseTest.java diff --git a/pom.xml b/pom.xml index 6f3e0d69896f..45d574e734f9 100644 --- a/pom.xml +++ b/pom.xml @@ -128,6 +128,7 @@ hexagonal abstract-document aggregator-microservices + promise page-object diff --git a/promise/pom.xml b/promise/pom.xml index ca12515ee846..f5727b9516cd 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -43,5 +43,10 @@ mockito-core test + + com.iluwatar + async-method-invocation + 1.13.0-SNAPSHOT + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index dc22c307a7dc..5817e68da2fd 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -1,24 +1,38 @@ package com.iluwatar.promise; +import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; + +/** + * + * Application that uses promise pattern. + */ public class App { /** * Program entry point * @param args arguments + * @throws InterruptedException if main thread is interruped. */ - public static void main(String[] args) { + public static void main(String[] args) throws InterruptedException { ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); - executor.execute(() -> { + + Promise consumedPromise = new Promise<>(); + consumedPromise.fulfillInAsync(() -> { Thread.sleep(1000); return 10; - }).then(value -> {System.out.println("Consumed the value: " + value);}) - .then(nullVal -> {System.out.println("Post consuming value");}); + }, executor).then(value -> { + System.out.println("Consumed int value: " + value); + }); - - executor.execute(() -> { + Promise transformedPromise = new Promise<>(); + transformedPromise.fulfillInAsync(() -> { Thread.sleep(1000); return "10"; - }).then(value -> {return 10 + Integer.parseInt(value);}) - .then(intValue -> {System.out.println("Consumed int value: " + intValue);}); + }, executor).then(value -> { return Integer.parseInt(value); }).then(value -> { + System.out.println(value); + }); + + consumedPromise.await(); + transformedPromise.await(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java b/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java deleted file mode 100644 index a68154e17923..000000000000 --- a/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.promise; - -import java.util.function.Consumer; -import java.util.function.Function; - -import com.iluwatar.async.method.invocation.AsyncResult; - -public interface ListenableAsyncResult extends AsyncResult { - ListenableAsyncResult then(Consumer action); - ListenableAsyncResult then(Function func); - ListenableAsyncResult error(Consumer action); -} diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java new file mode 100644 index 000000000000..0bc4accbb3da --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -0,0 +1,143 @@ +package com.iluwatar.promise; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.iluwatar.async.method.invocation.AsyncExecutor; +import com.iluwatar.async.method.invocation.internal.CompletableResult; + +/** + * Implements the promise pattern. + * @param type of result. + */ +public class Promise extends CompletableResult { + + private Runnable fulfillmentAction; + + /** + * Creates a promise that will be fulfilled in future. + */ + public Promise() { + super(null); + } + + /** + * Fulfills the promise with the provided value. + * @param value the fulfilled value that can be accessed using {@link #getValue()}. + */ + @Override + public void setValue(T value) { + super.setValue(value); + postComplete(); + } + + /** + * Fulfills the promise with exception due to error in execution. + * @param exception the exception will be wrapped in {@link ExecutionException} + * when accessing the value using {@link #getValue()}. + */ + @Override + public void setException(Exception exception) { + super.setException(exception); + postComplete(); + } + + void postComplete() { + if (fulfillmentAction == null) { + return; + } + fulfillmentAction.run(); + } + + /** + * Executes the task using the executor in other thread and fulfills the promise returned + * once the task completes either successfully or with an exception. + * + * @param task the task that will provide the value to fulfill the promise. + * @param executor the executor in which the task should be run. + * @return a promise that represents the result of running the task provided. + */ + public Promise fulfillInAsync(final Callable task, AsyncExecutor executor) { + executor.startProcess(new Callable() { + + @Override + public Void call() throws Exception { + setValue(task.call()); + return null; + } + }); + return this; + } + + /** + * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with + * result of this promise as argument to the action provided. + * @param action action to be executed. + * @return a new promise. + */ + public Promise then(Consumer action) { + Promise dest = new Promise<>(); + fulfillmentAction = new ConsumeAction(this, dest, action); + return dest; + } + + /** + * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with + * result of this promise as argument to the function provided. + * @param func function to be executed. + * @return a new promise. + */ + public Promise then(Function func) { + Promise dest = new Promise<>(); + fulfillmentAction = new FunctionAction(this, dest, func); + return dest; + } + + private class ConsumeAction implements Runnable { + + private Promise current; + private Promise dest; + private Consumer action; + + public ConsumeAction(Promise current, Promise dest, Consumer action) { + this.current = current; + this.dest = dest; + this.action = action; + } + + @Override + public void run() { + try { + action.accept(current.getValue()); + dest.setValue(null); + } catch (Throwable e) { + dest.setException((Exception) e.getCause()); + } + } + } + + private class FunctionAction implements Runnable { + + private Promise current; + private Promise dest; + private Function func; + + public FunctionAction(Promise current, Promise dest, Function func) { + this.current = current; + this.dest = dest; + this.func = func; + } + + @Override + public void run() { + try { + V result = func.apply(current.getValue()); + dest.setValue(result); + } catch (Throwable e) { + dest.setException((Exception) e.getCause()); + } + } + } +} diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java deleted file mode 100644 index eb43b05469c3..000000000000 --- a/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.iluwatar.promise; - -import java.util.concurrent.Callable; - -public interface PromiseAsyncExecutor { - ListenableAsyncResult execute(Callable task); -} diff --git a/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java deleted file mode 100644 index aa057d676264..000000000000 --- a/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.iluwatar.promise; - -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.function.Function; - -public class ThreadAsyncExecutor implements PromiseAsyncExecutor { - - /** Index for thread naming */ - private final AtomicInteger idx = new AtomicInteger(0); - - @Override - public ListenableAsyncResult execute(Callable task) { - Promise promise = new Promise<>(); - new Thread(() -> { - try { - promise.setValue(task.call()); - promise.postComplete(); - } catch (Exception ex) { - promise.setException(ex); - } - } , "executor-" + idx.incrementAndGet()).start(); - return promise; - } - - // TODO there is scope of extending the completable future from async method invocation project. Do that. - private class Promise implements ListenableAsyncResult { - - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; - - final Object lock; - volatile int state = RUNNING; - T value; - Exception exception; - Runnable fulfilmentAction; - - public Promise() { - this.lock = new Object(); - } - - void postComplete() { - fulfilmentAction.run(); - } - - /** - * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for - * completion. - * - * @param value - * value of the evaluated task - */ - public void setValue(T value) { - this.value = value; - this.state = COMPLETED; - synchronized (lock) { - lock.notifyAll(); - } - } - - /** - * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for - * completion. - * - * @param exception - * exception of the failed task - */ - public void setException(Exception exception) { - this.exception = exception; - this.state = FAILED; - synchronized (lock) { - lock.notifyAll(); - } - } - - @Override - public boolean isCompleted() { - return state > RUNNING; - } - - @Override - public T getValue() throws ExecutionException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - throw new IllegalStateException("Execution not completed yet"); - } - } - - @Override - public void await() throws InterruptedException { - synchronized (lock) { - if (!isCompleted()) { - lock.wait(); - } - } - } - - @Override - public ListenableAsyncResult then(Consumer action) { - Promise dest = new Promise<>(); - fulfilmentAction = new ConsumeAction(this, dest, action); - return dest; - } - - @Override - public ListenableAsyncResult then(Function func) { - Promise dest = new Promise<>(); - fulfilmentAction = new FunctionAction(this, dest, func); - return dest; - } - - @Override - public ListenableAsyncResult error(Consumer action) { - return null; - } - - private class ConsumeAction implements Runnable { - - private Promise current; - private Promise dest; - private Consumer action; - - public ConsumeAction(Promise current, Promise dest, Consumer action) { - this.current = current; - this.dest = dest; - this.action = action; - } - - @Override - public void run() { - try { - action.accept(current.getValue()); - dest.setValue(null); - } catch (ExecutionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - dest.postComplete(); - } - } - - private class FunctionAction implements Runnable { - - private Promise current; - private Promise dest; - private Function func; - - public FunctionAction(Promise current, Promise dest, Function func) { - this.current = current; - this.dest = dest; - this.func = func; - } - - @Override - public void run() { - try { - V result = func.apply(current.getValue()); - dest.setValue(result); - } catch (ExecutionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - dest.postComplete(); - } - } - } - } \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/AppTest.java b/promise/src/test/java/com/iluwatar/promise/AppTest.java new file mode 100644 index 000000000000..b59187cb15d2 --- /dev/null +++ b/promise/src/test/java/com/iluwatar/promise/AppTest.java @@ -0,0 +1,15 @@ +package com.iluwatar.promise; + +import org.junit.Test; + +/** + * + * Application test. + */ +public class AppTest { + + @Test + public void testApp() throws InterruptedException { + App.main(null); + } +} diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java new file mode 100644 index 000000000000..9c28be1b36bd --- /dev/null +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -0,0 +1,128 @@ +package com.iluwatar.promise; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; + +/** + * Tests Promise class. + */ +public class PromiseTest { + + private ThreadAsyncExecutor executor; + private Promise promise; + @Rule public ExpectedException exception = ExpectedException.none(); + + @Before + public void setUp() { + executor = new ThreadAsyncExecutor(); + promise = new Promise<>(); + } + + @Test + public void promiseIsFulfilledWithTheResultantValueOfExecutingTheTask() + throws InterruptedException, ExecutionException { + promise.fulfillInAsync(new NumberCrunchingTask(), executor); + + // await fulfillment + promise.await(); + + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.getValue()); + } + + @Test + public void dependentPromiseIsFulfilledAfterTheConsumerConsumesTheResultOfThisPromise() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(value -> { + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); + }); + + + // await fulfillment + dependentPromise.await(); + } + + @Test + public void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnException() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(new Consumer() { + + @Override + public void accept(Integer t) { + throw new RuntimeException("Barf!"); + } + }); + + + // await fulfillment + dependentPromise.await(); + + exception.expect(ExecutionException.class); + + dependentPromise.getValue(); + } + + @Test + public void dependentPromiseIsFulfilledAfterTheFunctionTransformsTheResultOfThisPromise() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(value -> { + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); + return String.valueOf(value); + }); + + + // await fulfillment + dependentPromise.await(); + + assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.getValue()); + } + + @Test + public void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsException() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(new Function() { + + @Override + public String apply(Integer t) { + throw new RuntimeException("Barf!"); + } + }); + + // await fulfillment + dependentPromise.await(); + + exception.expect(ExecutionException.class); + + dependentPromise.getValue(); + } + + private static class NumberCrunchingTask implements Callable { + + private static final Integer CRUNCHED_NUMBER = Integer.MAX_VALUE; + + @Override + public Integer call() throws Exception { + // Do number crunching + Thread.sleep(1000); + return CRUNCHED_NUMBER; + } + } +} From bc94d0fc26688a20480851f5c974f46396179a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 21 Jul 2016 20:21:36 +0300 Subject: [PATCH 118/207] Fix link --- event-driven-architecture/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md index c779fd430303..843e4c2684fe 100644 --- a/event-driven-architecture/README.md +++ b/event-driven-architecture/README.md @@ -2,7 +2,7 @@ layout: pattern title: Event Driven Architecture folder: event-driven-architecture -permalink: /patterns/event-driven-architecture +permalink: /patterns/event-driven-architecture/ categories: Architectural tags: - Java From 2b945ca27f991d03a5ffd58d4faa809d1aa8df23 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 22 Jul 2016 16:47:52 +0530 Subject: [PATCH 119/207] Work on #403, removed dependency on async method invocation module, added more tests --- promise/pom.xml | 5 - .../main/java/com/iluwatar/promise/App.java | 26 ++- .../java/com/iluwatar/promise/Promise.java | 169 ++++++++++++++---- .../java/com/iluwatar/promise/AppTest.java | 4 +- .../com/iluwatar/promise/PromiseTest.java | 147 ++++++++++++--- 5 files changed, 275 insertions(+), 76 deletions(-) diff --git a/promise/pom.xml b/promise/pom.xml index f5727b9516cd..ca12515ee846 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -43,10 +43,5 @@ mockito-core test - - com.iluwatar - async-method-invocation - 1.13.0-SNAPSHOT - diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 5817e68da2fd..f9e089f3de6d 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -1,6 +1,9 @@ package com.iluwatar.promise; -import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * @@ -12,10 +15,19 @@ public class App { * Program entry point * @param args arguments * @throws InterruptedException if main thread is interruped. + * @throws ExecutionException */ - public static void main(String[] args) throws InterruptedException { - ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); - + public static void main(String[] args) throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + promiseUsage(executor); + } finally { + executor.shutdownNow(); + } + } + + private static void promiseUsage(Executor executor) + throws InterruptedException, ExecutionException { Promise consumedPromise = new Promise<>(); consumedPromise.fulfillInAsync(() -> { Thread.sleep(1000); @@ -29,10 +41,10 @@ public static void main(String[] args) throws InterruptedException { Thread.sleep(1000); return "10"; }, executor).then(value -> { return Integer.parseInt(value); }).then(value -> { - System.out.println(value); + System.out.println("Consumed transformed int value: " + value); }); - consumedPromise.await(); - transformedPromise.await(); + consumedPromise.get(); + transformedPromise.get(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 0bc4accbb3da..991c2a05c64b 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -2,17 +2,18 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; -import com.iluwatar.async.method.invocation.AsyncExecutor; -import com.iluwatar.async.method.invocation.internal.CompletableResult; - /** * Implements the promise pattern. * @param type of result. */ -public class Promise extends CompletableResult { +public class Promise extends PromiseSupport { private Runnable fulfillmentAction; @@ -20,31 +21,30 @@ public class Promise extends CompletableResult { * Creates a promise that will be fulfilled in future. */ public Promise() { - super(null); } /** * Fulfills the promise with the provided value. - * @param value the fulfilled value that can be accessed using {@link #getValue()}. + * @param value the fulfilled value that can be accessed using {@link #get()}. */ @Override - public void setValue(T value) { - super.setValue(value); - postComplete(); + public void fulfill(T value) { + super.fulfill(value); + postFulfillment(); } /** * Fulfills the promise with exception due to error in execution. * @param exception the exception will be wrapped in {@link ExecutionException} - * when accessing the value using {@link #getValue()}. + * when accessing the value using {@link #get()}. */ @Override - public void setException(Exception exception) { - super.setException(exception); - postComplete(); + public void fulfillExceptionally(Exception exception) { + super.fulfillExceptionally(exception); + postFulfillment(); } - void postComplete() { + void postFulfillment() { if (fulfillmentAction == null) { return; } @@ -59,13 +59,12 @@ void postComplete() { * @param executor the executor in which the task should be run. * @return a promise that represents the result of running the task provided. */ - public Promise fulfillInAsync(final Callable task, AsyncExecutor executor) { - executor.startProcess(new Callable() { - - @Override - public Void call() throws Exception { - setValue(task.call()); - return null; + public Promise fulfillInAsync(final Callable task, Executor executor) { + executor.execute(() -> { + try { + fulfill(task.call()); + } catch (Exception e) { + fulfillExceptionally(e); } }); return this; @@ -91,18 +90,22 @@ public Promise then(Consumer action) { */ public Promise then(Function func) { Promise dest = new Promise<>(); - fulfillmentAction = new FunctionAction(this, dest, func); + fulfillmentAction = new TransformAction(this, dest, func); return dest; } + /** + * A consume action provides the action, the value from source promise and fulfills the + * destination promise. + */ private class ConsumeAction implements Runnable { - private Promise current; + private Promise src; private Promise dest; private Consumer action; - public ConsumeAction(Promise current, Promise dest, Consumer action) { - this.current = current; + ConsumeAction(Promise src, Promise dest, Consumer action) { + this.src = src; this.dest = dest; this.action = action; } @@ -110,22 +113,26 @@ public ConsumeAction(Promise current, Promise dest, Consumer @Override public void run() { try { - action.accept(current.getValue()); - dest.setValue(null); + action.accept(src.get()); + dest.fulfill(null); } catch (Throwable e) { - dest.setException((Exception) e.getCause()); + dest.fulfillExceptionally((Exception) e.getCause()); } } } - private class FunctionAction implements Runnable { + /** + * A function action provides transformation function, value from source promise and fulfills the + * destination promise with the transformed value. + */ + private class TransformAction implements Runnable { - private Promise current; + private Promise src; private Promise dest; private Function func; - public FunctionAction(Promise current, Promise dest, Function func) { - this.current = current; + TransformAction(Promise src, Promise dest, Function func) { + this.src = src; this.dest = dest; this.func = func; } @@ -133,11 +140,103 @@ public FunctionAction(Promise current, Promise dest, Function implements Future { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + + volatile int state = RUNNING; + T value; + Exception exception; + + PromiseSupport() { + this.lock = new Object(); + } + + void fulfill(T value) { + this.value = value; + this.state = COMPLETED; + synchronized (lock) { + lock.notifyAll(); + } + } + + void fulfillExceptionally(Exception exception) { + this.exception = exception; + this.state = FAILED; + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return state > RUNNING; + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(); + if (state == COMPLETED) { + return value; + } else { + throw new ExecutionException(exception); + } + } + } + } + + @Override + public T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(unit.toMillis(timeout)); + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new TimeoutException(); + } + } + } + } +} \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/AppTest.java b/promise/src/test/java/com/iluwatar/promise/AppTest.java index b59187cb15d2..b2628127c75e 100644 --- a/promise/src/test/java/com/iluwatar/promise/AppTest.java +++ b/promise/src/test/java/com/iluwatar/promise/AppTest.java @@ -1,5 +1,7 @@ package com.iluwatar.promise; +import java.util.concurrent.ExecutionException; + import org.junit.Test; /** @@ -9,7 +11,7 @@ public class AppTest { @Test - public void testApp() throws InterruptedException { + public void testApp() throws InterruptedException, ExecutionException { App.main(null); } } diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index 9c28be1b36bd..c64b82d06e7d 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -1,9 +1,16 @@ package com.iluwatar.promise; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; @@ -12,20 +19,18 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; - /** * Tests Promise class. */ public class PromiseTest { - private ThreadAsyncExecutor executor; + private Executor executor; private Promise promise; @Rule public ExpectedException exception = ExpectedException.none(); @Before public void setUp() { - executor = new ThreadAsyncExecutor(); + executor = Executors.newSingleThreadExecutor(); promise = new Promise<>(); } @@ -34,10 +39,70 @@ public void promiseIsFulfilledWithTheResultantValueOfExecutingTheTask() throws InterruptedException, ExecutionException { promise.fulfillInAsync(new NumberCrunchingTask(), executor); - // await fulfillment - promise.await(); + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.get()); + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + @Test + public void promiseIsFulfilledWithAnExceptionIfTaskThrowsAnException() + throws InterruptedException, ExecutionException, TimeoutException { + testWaitingForeverForPromiseToBeFulfilled(); + testWaitingSomeTimeForPromiseToBeFulfilled(); + } - assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.getValue()); + private void testWaitingForeverForPromiseToBeFulfilled() throws InterruptedException, TimeoutException { + Promise promise = new Promise<>(); + promise.fulfillInAsync(new Callable() { + + @Override + public Integer call() throws Exception { + throw new RuntimeException("Barf!"); + }}, executor); + + try { + promise.get(); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + promise.get(1000, TimeUnit.SECONDS); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + } + + private void testWaitingSomeTimeForPromiseToBeFulfilled() + throws InterruptedException, TimeoutException { + Promise promise = new Promise<>(); + promise.fulfillInAsync(new Callable() { + + @Override + public Integer call() throws Exception { + throw new RuntimeException("Barf!"); + }}, executor); + + try { + promise.get(1000, TimeUnit.SECONDS); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + promise.get(); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + } @Test @@ -50,13 +115,14 @@ public void dependentPromiseIsFulfilledAfterTheConsumerConsumesTheResultOfThisPr }); - // await fulfillment - dependentPromise.await(); + dependentPromise.get(); + assertTrue(dependentPromise.isDone()); + assertFalse(dependentPromise.isCancelled()); } @Test public void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnException() - throws InterruptedException, ExecutionException { + throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) .then(new Consumer() { @@ -67,13 +133,21 @@ public void accept(Integer t) { } }); - - // await fulfillment - dependentPromise.await(); - - exception.expect(ExecutionException.class); - - dependentPromise.getValue(); + try { + dependentPromise.get(); + fail("Fetching dependent promise should result in exception if the action threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + dependentPromise.get(1000, TimeUnit.SECONDS); + fail("Fetching dependent promise should result in exception if the action threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } } @Test @@ -87,15 +161,14 @@ public void dependentPromiseIsFulfilledAfterTheFunctionTransformsTheResultOfThis }); - // await fulfillment - dependentPromise.await(); - - assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.getValue()); + assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.get()); + assertTrue(dependentPromise.isDone()); + assertFalse(dependentPromise.isCancelled()); } @Test public void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsException() - throws InterruptedException, ExecutionException { + throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) .then(new Function() { @@ -106,12 +179,30 @@ public String apply(Integer t) { } }); - // await fulfillment - dependentPromise.await(); - - exception.expect(ExecutionException.class); - - dependentPromise.getValue(); + try { + dependentPromise.get(); + fail("Fetching dependent promise should result in exception if the function threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + dependentPromise.get(1000, TimeUnit.SECONDS); + fail("Fetching dependent promise should result in exception if the function threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + } + + @Test + public void fetchingAnAlreadyFulfilledPromiseReturnsTheFulfilledValueImmediately() + throws InterruptedException, ExecutionException, TimeoutException { + Promise promise = new Promise<>(); + promise.fulfill(NumberCrunchingTask.CRUNCHED_NUMBER); + + promise.get(1000, TimeUnit.SECONDS); } private static class NumberCrunchingTask implements Callable { From eb560f5f54b6a928e978b81549d7369f957364a0 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 22 Jul 2016 16:53:01 +0530 Subject: [PATCH 120/207] Work on #403, removed checkstyle violations --- .../main/java/com/iluwatar/promise/App.java | 2 +- .../java/com/iluwatar/promise/Promise.java | 95 ------------------- .../com/iluwatar/promise/PromiseTest.java | 6 +- 3 files changed, 5 insertions(+), 98 deletions(-) diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index f9e089f3de6d..3390f2a234da 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -15,7 +15,7 @@ public class App { * Program entry point * @param args arguments * @throws InterruptedException if main thread is interruped. - * @throws ExecutionException + * @throws ExecutionException if an execution error occurs. */ public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executor = Executors.newSingleThreadExecutor(); diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 991c2a05c64b..03977c541bfc 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -3,9 +3,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; @@ -147,96 +144,4 @@ public void run() { } } } -} - - -/** - * A really simplified implementation of future that allows completing it successfully with a value - * or exceptionally with an exception. - */ -class PromiseSupport implements Future { - - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; - - final Object lock; - - volatile int state = RUNNING; - T value; - Exception exception; - - PromiseSupport() { - this.lock = new Object(); - } - - void fulfill(T value) { - this.value = value; - this.state = COMPLETED; - synchronized (lock) { - lock.notifyAll(); - } - } - - void fulfillExceptionally(Exception exception) { - this.exception = exception; - this.state = FAILED; - synchronized (lock) { - lock.notifyAll(); - } - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return state > RUNNING; - } - - @Override - public T get() throws InterruptedException, ExecutionException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - synchronized (lock) { - lock.wait(); - if (state == COMPLETED) { - return value; - } else { - throw new ExecutionException(exception); - } - } - } - } - - @Override - public T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - synchronized (lock) { - lock.wait(unit.toMillis(timeout)); - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - throw new TimeoutException(); - } - } - } - } } \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index c64b82d06e7d..842558589599 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -58,7 +58,8 @@ private void testWaitingForeverForPromiseToBeFulfilled() throws InterruptedExcep @Override public Integer call() throws Exception { throw new RuntimeException("Barf!"); - }}, executor); + } + }, executor); try { promise.get(); @@ -85,7 +86,8 @@ private void testWaitingSomeTimeForPromiseToBeFulfilled() @Override public Integer call() throws Exception { throw new RuntimeException("Barf!"); - }}, executor); + } + }, executor); try { promise.get(1000, TimeUnit.SECONDS); From 09ba5ca656dd17ac5c2d518452ad5f89df388e57 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 22 Jul 2016 18:34:05 +0530 Subject: [PATCH 121/207] Work on #403, added class diagrams and javadocs --- promise/etc/promise.png | Bin 0 -> 59544 bytes promise/etc/promise.ucls | 115 +++++++++++++++++ .../main/java/com/iluwatar/promise/App.java | 44 ++++++- .../java/com/iluwatar/promise/Promise.java | 25 +++- .../com/iluwatar/promise/PromiseSupport.java | 119 ++++++++++++++++++ .../java/com/iluwatar/promise/AppTest.java | 22 ++++ .../com/iluwatar/promise/PromiseTest.java | 22 ++++ 7 files changed, 344 insertions(+), 3 deletions(-) create mode 100644 promise/etc/promise.png create mode 100644 promise/etc/promise.ucls create mode 100644 promise/src/main/java/com/iluwatar/promise/PromiseSupport.java diff --git a/promise/etc/promise.png b/promise/etc/promise.png new file mode 100644 index 0000000000000000000000000000000000000000..1a0f671080915d26268f2ce16df59c430a14926e GIT binary patch literal 59544 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 literal 0 HcmV?d00001 diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls new file mode 100644 index 000000000000..fe3c6c2e01f5 --- /dev/null +++ b/promise/etc/promise.ucls @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 3390f2a234da..6ce7de99472a 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -1,5 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 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.promise; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -7,14 +30,31 @@ /** * - * Application that uses promise pattern. + *

The Promise object is used for asynchronous computations. A Promise represents an operation that + * hasn't completed yet, but is expected in the future. + * + *

A Promise represents a proxy for a value not necessarily known when the promise is created. It + * allows you to associate dependent promises to an asynchronous action's eventual success value or + * failure reason. This lets asynchronous methods return values like synchronous methods: instead of the final + * value, the asynchronous method returns a promise of having a value at some point in the future. + * + *

Promises provide a few advantages over callback objects: + *

+ * + * @see CompletableFuture */ public class App { + private App() { + } + /** * Program entry point * @param args arguments - * @throws InterruptedException if main thread is interruped. + * @throws InterruptedException if main thread is interrupted. * @throws ExecutionException if an execution error occurs. */ public static void main(String[] args) throws InterruptedException, ExecutionException { diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 03977c541bfc..fe7dc6f9fd99 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.promise; import java.util.concurrent.Callable; @@ -8,6 +30,7 @@ /** * Implements the promise pattern. + * * @param type of result. */ public class Promise extends PromiseSupport { @@ -41,7 +64,7 @@ public void fulfillExceptionally(Exception exception) { postFulfillment(); } - void postFulfillment() { + private void postFulfillment() { if (fulfillmentAction == null) { return; } diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java new file mode 100644 index 000000000000..dde2ca4522f3 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java @@ -0,0 +1,119 @@ +/** + * The MIT License + * Copyright (c) 2014 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.promise; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A really simplified implementation of future that allows completing it successfully with a value + * or exceptionally with an exception. + */ +class PromiseSupport implements Future { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + + volatile int state = RUNNING; + T value; + Exception exception; + + PromiseSupport() { + this.lock = new Object(); + } + + void fulfill(T value) { + this.value = value; + this.state = COMPLETED; + synchronized (lock) { + lock.notifyAll(); + } + } + + void fulfillExceptionally(Exception exception) { + this.exception = exception; + this.state = FAILED; + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return state > RUNNING; + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(); + if (state == COMPLETED) { + return value; + } else { + throw new ExecutionException(exception); + } + } + } + } + + @Override + public T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(unit.toMillis(timeout)); + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new TimeoutException(); + } + } + } + } +} \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/AppTest.java b/promise/src/test/java/com/iluwatar/promise/AppTest.java index b2628127c75e..1d1cb061df2a 100644 --- a/promise/src/test/java/com/iluwatar/promise/AppTest.java +++ b/promise/src/test/java/com/iluwatar/promise/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.promise; import java.util.concurrent.ExecutionException; diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index 842558589599..de0ecb6d73f8 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.promise; import static org.junit.Assert.assertEquals; From 40ac5525421fa2932224048fce76aded3fe3f332 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 24 Jul 2016 01:45:49 +0530 Subject: [PATCH 122/207] Work on #403, added README --- promise/README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 promise/README.md diff --git a/promise/README.md b/promise/README.md new file mode 100644 index 000000000000..069a8dbfeaa5 --- /dev/null +++ b/promise/README.md @@ -0,0 +1,45 @@ +--- +layout: pattern +title: Promise +folder: promise +permalink: /patterns/promise/ +categories: Structural +tags: + - Java + - Concurrency + - Difficulty-Intermediate +--- + +## Also known as +CompletableFuture + +## Intent +A Promise represents a proxy for a value not necessarily known when the promise is created. It +allows you to associate dependent promises to an asynchronous action's eventual success value or +failure reason. Promises are a way to write async code that still appears as though it is executing +in a synchronous way. + +![alt text](./etc/promise.png "Promise") + +## Applicability +Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously +and: + +* code maintainablity and readability suffers due to callback hell. +* you need to compose promises and need better error handling for asynchronous tasks. +* you want to use functional style of programming. + + +## Real world examples + +* [java.util.concurrent.CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) +* [Guava ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained) + +## Related Patterns + * Async Method Invocation + * Callback + +## Credits + +* [You are missing the point to Promises](https://gist.github.com/domenic/3889970) +* [Functional style callbacks using CompleteableFuture](https://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture) From 5fcef89384835575a5ff02429fc3da3ac5a78ace Mon Sep 17 00:00:00 2001 From: Kamil Pietruszka Date: Tue, 26 Jul 2016 20:33:43 +0200 Subject: [PATCH 123/207] #459 added known aliast for monostate pattern --- monostate/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monostate/README.md b/monostate/README.md index 415d13f0e7f9..3576dc659313 100644 --- a/monostate/README.md +++ b/monostate/README.md @@ -9,6 +9,9 @@ tags: - Difficulty-Beginner --- +## Also known as +Borg + ## Intent Enforces a behaviour like sharing the same state amongst all instances. From 94c3a2caf3cccba03197a53fb6c3e03190036d8f Mon Sep 17 00:00:00 2001 From: Sumit Yadav Date: Wed, 3 Aug 2016 15:02:46 +0530 Subject: [PATCH 124/207] removed extra "is" from the javadoc of InitializingOnDemandHolderIdiom class --- .../com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 4aa6afe1203e..0c450c60c10d 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -26,7 +26,7 @@ * The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton * object in Java. *

- * The technique is is as lazy as possible and works in all known versions of Java. It takes advantage + * The technique is as lazy as possible and works in all known versions of Java. It takes advantage * of language guarantees about class initialization, and will therefore work correctly in all * Java-compliant compilers and virtual machines. *

From 76970633b8a654e05d4341e96ee639bc3bcde969 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 4 Aug 2016 18:10:50 +0530 Subject: [PATCH 125/207] Work on #403, incorporate review changes --- promise/README.md | 5 +- promise/etc/promise.ucls | 30 ++++---- .../main/java/com/iluwatar/promise/App.java | 73 +++++++++++++++---- .../java/com/iluwatar/promise/Promise.java | 16 ++-- .../com/iluwatar/promise/PromiseSupport.java | 14 ++-- 5 files changed, 92 insertions(+), 46 deletions(-) diff --git a/promise/README.md b/promise/README.md index 069a8dbfeaa5..638bb3ef5327 100644 --- a/promise/README.md +++ b/promise/README.md @@ -3,10 +3,11 @@ layout: pattern title: Promise folder: promise permalink: /patterns/promise/ -categories: Structural +categories: Concurrency tags: - Java - - Concurrency + - Functional + - Reactive - Difficulty-Intermediate --- diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls index fe3c6c2e01f5..cdfb6ed7fb5d 100644 --- a/promise/etc/promise.ucls +++ b/promise/etc/promise.ucls @@ -74,37 +74,37 @@ - + + + + + - + + + + + - - - - - - + + - + - - - - - + - + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 6ce7de99472a..3a1ecfa01b80 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -22,6 +22,15 @@ */ package com.iluwatar.promise; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -44,6 +53,8 @@ *

  • Prevents callback hell and provides callback aggregation * * + *

    + * * @see CompletableFuture */ public class App { @@ -68,23 +79,57 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc private static void promiseUsage(Executor executor) throws InterruptedException, ExecutionException { - Promise consumedPromise = new Promise<>(); - consumedPromise.fulfillInAsync(() -> { - Thread.sleep(1000); - return 10; - }, executor).then(value -> { - System.out.println("Consumed int value: " + value); + String urlString = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; + Promise lineCountPromise = new Promise().fulfillInAsync(() -> { + return downloadFile(urlString); + }, executor).then(fileLocation -> { + return countLines(fileLocation); }); - Promise transformedPromise = new Promise<>(); - transformedPromise.fulfillInAsync(() -> { - Thread.sleep(1000); - return "10"; - }, executor).then(value -> { return Integer.parseInt(value); }).then(value -> { - System.out.println("Consumed transformed int value: " + value); + Promise> charFrequencyPromise = new Promise().fulfillInAsync(() -> { + return String.valueOf(downloadFile(urlString)); + }, executor).then(fileLocation -> { + return characterFrequency(fileLocation); }); - consumedPromise.get(); - transformedPromise.get(); + lineCountPromise.get(); + System.out.println("Line count is: " + lineCountPromise.get()); + charFrequencyPromise.get(); + System.out.println("Char frequency is: " + charFrequencyPromise.get()); + } + + private static Map characterFrequency(String fileLocation) { + // TODO Auto-generated method stub + return null; + } + + private static Integer countLines(String fileLocation) { + int lineCount = 0; + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader);) { + for (String line; (line = bufferedReader.readLine()) != null; ) { + lineCount++; + } + } catch (IOException ex) { + ex.printStackTrace(); + } + return lineCount; + } + + private static String downloadFile(String urlString) throws InterruptedException, IOException { + URL url = new URL(urlString); + File file = File.createTempFile("promise_pattern", null); + try (Reader reader = new InputStreamReader(url.openStream()); + BufferedReader bufferedReader = new BufferedReader(reader); + FileWriter writer = new FileWriter(file)) { + for (String line; (line = bufferedReader.readLine()) != null; ) { + writer.write(line); + writer.write("\n"); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + System.out.println("File downloaded at: " + file.getAbsolutePath()); + return file.getAbsolutePath(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index fe7dc6f9fd99..7d8a97e844e6 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -120,11 +120,11 @@ public Promise then(Function func) { */ private class ConsumeAction implements Runnable { - private Promise src; - private Promise dest; - private Consumer action; + private final Promise src; + private final Promise dest; + private final Consumer action; - ConsumeAction(Promise src, Promise dest, Consumer action) { + private ConsumeAction(Promise src, Promise dest, Consumer action) { this.src = src; this.dest = dest; this.action = action; @@ -147,11 +147,11 @@ public void run() { */ private class TransformAction implements Runnable { - private Promise src; - private Promise dest; - private Function func; + private final Promise src; + private final Promise dest; + private final Function func; - TransformAction(Promise src, Promise dest, Function func) { + private TransformAction(Promise src, Promise dest, Function func) { this.src = src; this.dest = dest; this.func = func; diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java index dde2ca4522f3..048586e23f95 100644 --- a/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java +++ b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java @@ -33,15 +33,15 @@ */ class PromiseSupport implements Future { - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; + private static final int RUNNING = 1; + private static final int FAILED = 2; + private static final int COMPLETED = 3; - final Object lock; + private final Object lock; - volatile int state = RUNNING; - T value; - Exception exception; + private volatile int state = RUNNING; + private T value; + private Exception exception; PromiseSupport() { this.lock = new Object(); From d484e7f731fe36832b4dd3ee690c1180bcc6117b Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 5 Aug 2016 14:38:25 +0530 Subject: [PATCH 126/207] Documented singleton double check idiom, explaining the dynamics that happen on each step for better understanding. Did this due to a PR #475 --- .../iluwatar/singleton/ThreadSafeDoubleCheckLocking.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java index 50203609c9a3..a8fd9648a0e9 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java @@ -53,11 +53,20 @@ private ThreadSafeDoubleCheckLocking() { public static ThreadSafeDoubleCheckLocking getInstance() { // local variable increases performance by 25 percent // Joshua Bloch "Effective Java, Second Edition", p. 283-284 + ThreadSafeDoubleCheckLocking result = instance; + // Check if singleton instance is initialized. If it is initialized then we can return the instance. if (result == null) { + // It is not initialized but we cannot be sure because some other thread might have initialized it + // in the meanwhile. So to make sure we need to lock on an object to get mutual exclusion. synchronized (ThreadSafeDoubleCheckLocking.class) { + // Again assign the instance to local variable to check if it was initialized by some other thread + // while current thread was blocked to enter the locked zone. If it was initialized then we can + // return the previously created instance just like the previous null check. result = instance; if (result == null) { + // The instance is still not initialized so we can safely (no other thread can enter this zone) + // create an instance and make it our singleton instance. instance = result = new ThreadSafeDoubleCheckLocking(); } } From ffbc5f2f295784b30073b9b339518c590b95397a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 13 Aug 2016 17:08:57 +0300 Subject: [PATCH 127/207] Reorganize LotteryNumbers for easier inclusion in the blog --- .../hexagonal/domain/LotteryNumbers.java | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java index 6f54e743b1ad..69d6546578a9 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -79,36 +79,6 @@ public Set getNumbers() { return Collections.unmodifiableSet(numbers); } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((numbers == null) ? 0 : numbers.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - LotteryNumbers other = (LotteryNumbers) obj; - if (numbers == null) { - if (other.numbers != null) { - return false; - } - } else if (!numbers.equals(other.numbers)) { - return false; - } - return true; - } - /** * Generates 4 unique random numbers between 1-20 into numbers set. */ @@ -122,7 +92,7 @@ private void generateRandomNumbers() { } } } - + /** * * Helper class for generating random numbers. @@ -149,4 +119,34 @@ public int nextInt() { return randomIterator.nextInt(); } } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((numbers == null) ? 0 : numbers.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryNumbers other = (LotteryNumbers) obj; + if (numbers == null) { + if (other.numbers != null) { + return false; + } + } else if (!numbers.equals(other.numbers)) { + return false; + } + return true; + } } From 7c2f5da9260357437a884d2117ae5d292d8eb800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 13 Aug 2016 18:28:53 +0300 Subject: [PATCH 128/207] Add final keyword --- hexagonal/src/main/java/com/iluwatar/hexagonal/App.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 3ae55b706c25..0f7239a89f8a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -69,7 +69,7 @@ */ public class App { - private static List allPlayerDetails; + private static final List allPlayerDetails; static { allPlayerDetails = new ArrayList<>(); From 0b36a3153d6b4aef7604b26259ca03fa89a3b4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 14 Aug 2016 22:42:59 +0300 Subject: [PATCH 129/207] Fix checkstyle error --- .../main/java/com/iluwatar/hexagonal/App.java | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 0f7239a89f8a..92ebf3572d81 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -69,53 +69,53 @@ */ public class App { - private static final List allPlayerDetails; + private static final List PLAYERS; static { - allPlayerDetails = new ArrayList<>(); - allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); - allPlayerDetails.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); - allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); - allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); - allPlayerDetails.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); - allPlayerDetails.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); - allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); - allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); - allPlayerDetails.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); - allPlayerDetails.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); - allPlayerDetails.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); - allPlayerDetails.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); - allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); - allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); - allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); - allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); - allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); - allPlayerDetails.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); - allPlayerDetails.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); - allPlayerDetails.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); - allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); - allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); - allPlayerDetails.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); - allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); - allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); - allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); - allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); - allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); - allPlayerDetails.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); - allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); - allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); - allPlayerDetails.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); - allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); - allPlayerDetails.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); - allPlayerDetails.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); - allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); - allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + PLAYERS = new ArrayList<>(); + PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); WireTransfersImpl wireTransfers = new WireTransfersImpl(); Random random = new Random(); - for (int i = 0; i < allPlayerDetails.size(); i++) { - wireTransfers.setFunds(allPlayerDetails.get(i).getBankAccount(), + for (int i = 0; i < PLAYERS.size(); i++) { + wireTransfers.setFunds(PLAYERS.get(i).getBankAccount(), random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); } } @@ -145,7 +145,7 @@ private static void submitTickets(LotteryService lotteryService, int numTickets) private static PlayerDetails getRandomPlayerDetails() { Random random = new Random(); - int idx = random.nextInt(allPlayerDetails.size()); - return allPlayerDetails.get(idx); + int idx = random.nextInt(PLAYERS.size()); + return PLAYERS.get(idx); } } From b16d7fc970538c0f8194a5dc7db567d0e8b16546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 14 Aug 2016 23:00:27 +0300 Subject: [PATCH 130/207] Configure Travis notification email --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index deb436cd2db3..bcbad682743d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,8 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh +notifications: + email: + - iluwatar@gmail.com + sudo: false # route the build to the container-based infrastructure for a faster build From 7e77216919c0c5cef6e67e477c2ee93ee16f25f8 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Mon, 15 Aug 2016 11:53:39 +0200 Subject: [PATCH 131/207] remove link, resolves #479 to avoid confusion, the link is removed. For more information on why this is done please look at the referenced issue --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 811d6a17ab71..1789398ec2df 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ are familiar with the patterns. # Getting started Before you dive into the material, you should be familiar with various -[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). +Programming/Software Design Principles. All designs should be as simple as possible. You should start with KISS, YAGNI, and Do The Simplest Thing That Could Possibly Work principles. Complexity and From 56100927a9537831e6d73d20d3a43840f1fc2d9b Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Mon, 15 Aug 2016 12:33:24 +0200 Subject: [PATCH 132/207] Addendum #481 Change page-index for faq.md --- faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faq.md b/faq.md index 69f7b795ee92..2280b42f5887 100644 --- a/faq.md +++ b/faq.md @@ -3,7 +3,7 @@ layout: page title: FAQ permalink: /faq/ icon: fa-question -page-index: 1 +page-index: 5 --- ### Q1: What is the difference between State and Strategy patterns? {#Q1} From c79df708b14115176721aa8dbda1ccd97f88da37 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 13:17:53 +0530 Subject: [PATCH 133/207] #211 added real world examples from Java api for creational patterns --- abstract-factory/README.md | 4 +++- builder/README.md | 3 +++ factory-method/README.md | 12 ++++++++---- singleton/README.md | 3 +++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/abstract-factory/README.md b/abstract-factory/README.md index 485599b984fb..fd669551d2e3 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -4,7 +4,7 @@ title: Abstract Factory folder: abstract-factory permalink: /patterns/abstract-factory/ categories: Creational -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -30,6 +30,8 @@ Use the Abstract Factory pattern when ## Real world examples * [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 diff --git a/builder/README.md b/builder/README.md index 5d1f3d24d469..9f374d94d376 100644 --- a/builder/README.md +++ b/builder/README.md @@ -26,6 +26,9 @@ Use the Builder pattern when ## Real world examples * [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) +* [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-) as well as similar buffers such as FloatBuffer, IntBuffer and so on. +* [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-) +* All implementations of [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html) * [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) ## Credits diff --git a/factory-method/README.md b/factory-method/README.md index 05549cf4f270..c379e96094c2 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -4,7 +4,7 @@ title: Factory Method folder: factory-method permalink: /patterns/factory-method/ categories: Creational -tags: +tags: - Java - Difficulty-Beginner - Gang Of Four @@ -29,9 +29,13 @@ Use the Factory Method pattern when ## Known uses -* java.util.Calendar -* java.util.ResourceBundle -* java.text.NumberFormat#getInstance() +* [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) +* [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) +* [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) +* [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) +* [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-) +* [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-) +* [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) ## Credits diff --git a/singleton/README.md b/singleton/README.md index 2a481f5c8e10..f821752ba22d 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -31,6 +31,9 @@ Use the Singleton pattern when ## Real world examples * [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +* [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) +* [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) + ## Credits From eb75773891360ff8f872fa4aea0fd3acb2f6c034 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 19:44:48 +0530 Subject: [PATCH 134/207] Added FAQ on Memento pattern Difference between java Serialization and Memento pattern added --- faq.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/faq.md b/faq.md index 2280b42f5887..01a7e7b7235d 100644 --- a/faq.md +++ b/faq.md @@ -65,3 +65,11 @@ Flyweight. ### Q7: What are the differences between FluentInterface and Builder patterns? {#Q7} Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change. + +### Q8: What is the difference between java.io.Serialization and Memento pattern? {#Q8} + +Memento is typically used to implement rollback/save-point support. Example we might want to mark the state of an object at a point in time, do some work and then decide to rollback to the previous state. + +On the other hand serialization may be used as a tool to save the state of an object into byte[] and preserving the contents in memory or disk. When someone invokes the memento to revert object's previous state then we can deserialize the information stored and recreate previous state. + +So Memento is a pattern and serialization is a tool that can be used to implement this pattern. Other ways to implement the pattern can be to clone the contents of the object and keep track of those clones. From a0c77c32b58dec68b75a65ed8de4e0eff966ae15 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 20:49:28 +0530 Subject: [PATCH 135/207] #211 added further examples for structural and behavioral patterns --- adapter/README.md | 4 ++++ chain/README.md | 1 + command/README.md | 3 ++- decorator/README.md | 8 ++++++++ flyweight/README.md | 4 ++-- interpreter/README.md | 9 ++++++++- iterator/README.md | 3 ++- mediator/README.md | 10 +++++++++- observer/README.md | 4 +++- state/README.md | 5 +++++ visitor/README.md | 3 +++ 11 files changed, 47 insertions(+), 7 deletions(-) diff --git a/adapter/README.md b/adapter/README.md index ea3baa7fa91c..6f517127f2ff 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -30,6 +30,10 @@ Use the Adapter pattern when ## Real world examples * [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-) + ## Credits diff --git a/chain/README.md b/chain/README.md index ef18f6f64ca5..0bf73fb929ca 100644 --- a/chain/README.md +++ b/chain/README.md @@ -28,6 +28,7 @@ Use Chain of Responsibility when * [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) * [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) +* [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-) ## Credits diff --git a/command/README.md b/command/README.md index a5478394cda1..987ec6a34fdc 100644 --- a/command/README.md +++ b/command/README.md @@ -4,7 +4,7 @@ title: Command folder: command permalink: /patterns/command/ categories: Behavioral -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -40,6 +40,7 @@ Use the Command pattern when you want to * [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) * [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) +* [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html) ## Credits diff --git a/decorator/README.md b/decorator/README.md index 63795114c798..9acf417adaa1 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -27,6 +27,14 @@ Use Decorator * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing +## Real World examples + * [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html), + [java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) and [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html) + * [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-) + * [java.util.Collections#unmodifiableXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableCollection-java.util.Collection-) + * [java.util.Collections#checkedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#checkedCollection-java.util.Collection-java.lang.Class-) + + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/flyweight/README.md b/flyweight/README.md index a98dced8e4e3..5bb8eb3805cc 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -4,7 +4,7 @@ title: Flyweight folder: flyweight permalink: /patterns/flyweight/ categories: Structural -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -30,7 +30,7 @@ true ## Real world examples -* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) +* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) and similarly for Byte, Character and other wrapped types. ## Credits diff --git a/interpreter/README.md b/interpreter/README.md index 87c1c47f7892..4fcc5e6ff55e 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -4,7 +4,7 @@ title: Interpreter folder: interpreter permalink: /patterns/interpreter/ categories: Behavioral -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -25,6 +25,13 @@ trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable +## Real World Applications +* [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) +* [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) +* All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) +* [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html) + + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/iterator/README.md b/iterator/README.md index d6be7758dda1..723e7f03cd20 100644 --- a/iterator/README.md +++ b/iterator/README.md @@ -4,7 +4,7 @@ title: Iterator folder: iterator permalink: /patterns/iterator/ categories: Behavioral -tags: +tags: - Java - Difficulty-Beginner - Gang Of Four @@ -29,6 +29,7 @@ Use the Iterator pattern ## Real world examples * [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) +* [java.util.Enumeration](http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html) ## Credits diff --git a/mediator/README.md b/mediator/README.md index c7a0478c8c66..5a784bbcbe0d 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -24,6 +24,14 @@ Use the Mediator pattern when * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing +## Real World Applications + +* All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) +* [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-) +* submit() and invokeXXX() methods of [java.util.concurrent.ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) +* scheduleXXX() methods of [java.util.concurrent.ScheduledExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html) +* [java.lang.reflect.Method#invoke()](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#invoke-java.lang.Object-java.lang.Object...-) + ## 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](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/observer/README.md b/observer/README.md index 6fbe3cdab9fd..e25359f0245f 100644 --- a/observer/README.md +++ b/observer/README.md @@ -4,7 +4,7 @@ title: Observer folder: observer permalink: /patterns/observer/ categories: Behavioral -tags: +tags: - Java - Difficulty-Beginner - Gang Of Four @@ -35,6 +35,8 @@ Use the Observer pattern in any of the following situations ## Real world examples * [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) +* [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html) +* [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html) ## Credits diff --git a/state/README.md b/state/README.md index f5cb189fde44..948c62a089d5 100644 --- a/state/README.md +++ b/state/README.md @@ -25,6 +25,11 @@ Use the State pattern in either of the following cases * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. +## Real world applications + +* [javax.faces.lifecycle.Lifecycle#execute()](http://docs.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html#execute-javax.faces.context.FacesContext-) controlled by [FacesServlet](http://docs.oracle.com/javaee/7/api/javax/faces/webapp/FacesServlet.html), the behavior is dependent on current phase of lifecycle. +* [JDiameter - Diameter State Machine](https://github.com/npathai/jdiameter/blob/master/core/jdiameter/api/src/main/java/org/jdiameter/api/app/State.java) + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/visitor/README.md b/visitor/README.md index c1e24a624c23..bda789a185c2 100644 --- a/visitor/README.md +++ b/visitor/README.md @@ -27,6 +27,9 @@ Use the Visitor pattern when ## Real world examples * [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) +* [javax.lang.model.element.AnnotationValue](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValue.html) and [AnnotationValueVisitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValueVisitor.html) +* [javax.lang.model.element.Element](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html) and [Element Visitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/ElementVisitor.html) +* [java.nio.file.FileVisitor](http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitor.html) ## Credits From 28647cdf482e878bb28ae535ae9c23cc1992f8a5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 20:57:48 +0530 Subject: [PATCH 136/207] #211, consistent use of real world examples section in all readme files. --- decorator/README.md | 4 ++-- interpreter/README.md | 2 +- mediator/README.md | 2 +- state/README.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/decorator/README.md b/decorator/README.md index 9acf417adaa1..08f8696450d9 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -27,13 +27,13 @@ Use Decorator * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing -## Real World examples +## Real world examples * [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html), [java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) and [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html) * [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-) * [java.util.Collections#unmodifiableXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableCollection-java.util.Collection-) * [java.util.Collections#checkedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#checkedCollection-java.util.Collection-java.lang.Class-) - + ## Credits diff --git a/interpreter/README.md b/interpreter/README.md index 4fcc5e6ff55e..be65179627c9 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -25,7 +25,7 @@ trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable -## Real World Applications +## Real world examples * [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) * [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) * All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) diff --git a/mediator/README.md b/mediator/README.md index 5a784bbcbe0d..3452082ef8e6 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -24,7 +24,7 @@ Use the Mediator pattern when * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing -## Real World Applications +## Real world examples * All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) * [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-) diff --git a/state/README.md b/state/README.md index 948c62a089d5..549afa61f9a3 100644 --- a/state/README.md +++ b/state/README.md @@ -25,7 +25,7 @@ Use the State pattern in either of the following cases * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. -## Real world applications +## Real world examples * [javax.faces.lifecycle.Lifecycle#execute()](http://docs.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html#execute-javax.faces.context.FacesContext-) controlled by [FacesServlet](http://docs.oracle.com/javaee/7/api/javax/faces/webapp/FacesServlet.html), the behavior is dependent on current phase of lifecycle. * [JDiameter - Diameter State Machine](https://github.com/npathai/jdiameter/blob/master/core/jdiameter/api/src/main/java/org/jdiameter/api/app/State.java) From 95cf9fe367c7c7c58e80f9b35ef5db0ca00c63e5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 22 Aug 2016 18:43:29 +0530 Subject: [PATCH 137/207] Work on #403, made example readable and moved methods into utility --- promise/etc/promise.png | Bin 59544 -> 55725 bytes promise/etc/promise.ucls | 30 ++--- .../main/java/com/iluwatar/promise/App.java | 127 ++++++++++-------- .../java/com/iluwatar/promise/Utility.java | 91 +++++++++++++ 4 files changed, 172 insertions(+), 76 deletions(-) create mode 100644 promise/src/main/java/com/iluwatar/promise/Utility.java diff --git a/promise/etc/promise.png b/promise/etc/promise.png index 1a0f671080915d26268f2ce16df59c430a14926e..0aef198acb4e9abdb4cf5b34f61869b46e870c8a 100644 GIT binary patch literal 55725 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 B1!4dI literal 59544 zcmd3OWmJ`2*Y#0v!9YvmG18P z)&)L~-cP*a9pCtVeEx9^;GDD1-fOQl*PL@*UQ!Z5XHF5FLZMJ+M1=3lpiqCEN1={7 zpEwFXp}3j(427D|6uHkQXCFB;a9T}HZ?Ae$MK&j+0n@%3u+B_-@0_sok;0Vc-_IQ9 zKeCwlGWKE0iI*o%ogO4-xhTF&|LoPJulM=V@Y6DE+xCjm^a`m|-X`AIkBNR8rH}R2 z|Ec!EvMV=(Mw3IMRii_dBR7|3V`R9H=n?qvSQFncNBAFwN@Za_2LC--@*_ljX7v5+ z9Jb{~fJ(j|CVAvD1gvDM^%QAp$m%pc=w%mU!K^16*;mt8<%Iao048i%+}a4PL`SZDXV<% zRqgTw+8eX%2M z8!l)m@;X?~<`2zLmm{z3xUfuxx?D98{$pn|J%5l$U#HD=ws#_1MK1hDXU0u_5Bq|G z?%p0{GV&Lq#nzk_H~1&p62;qOlRiDNZf?Y3XHRk2vOaxwZE3}NVrWSD%ja0W%qFho z_U9+dvhAw8ud$biT@p*F8H?sl;o*66_H0OBJ_Qz*BhQ|F)L?1xBV2w=yof9FWmVPJ zQJjIfP0K$#C*QoedhDoUieTCRY{-)-FJyakzSb{fxtRt8=LZI^hR|8f_fs|Zy!R(0 zlZa0c4-{98?7_aXKiZy?U1#q95`vt-}xnv0DhT$|TyPgkytU|UWwGu>?+k1?mD z#`ujE<56JY+RAP@*_XI(ELcwV=T0tnWnQH&b>n<5@VZJgwWUDR);62$oZ)7Z8#B4z zqZpx-O9YuKt2V*3AI*opWUJKRpri&EV~#%2_6{Q?!&;eWnVjjlI-2`P7fX3vND!-_ zu*bp4&TXmI@e=(^Kx0-(s~|_oYUhV6oko{EOv4Y`P*6(H@BVoOI0c9vYi!g@QpWS z?JdVd(9)`?mxRUTxZCO@M;A=EwA>~aex2TIC0+UYl)Kz~ItHg#(aVeQJSnfBP0*S{ zyl}}$-1X`9$x(sVH(a-3QWn!n#$Y{ysXXcNqdmy!}F_-19YCnu-f#&)B!exa6P`pOkrz2)x}@;Fma%tSxtLW&Ff2GYo$L%%FCE;dyUVS6(sinxfF|vdy1J=B6A6o1Kgz&>cyg_QttZcz zH^iSwY>I<(M_a$Zc2=tIQ$r9NZ7tr=V43^W?|k zE>Aey1g|iNz;zvdh+Nl&D6=sc2na3OgCk*fFT4l`bon%LwLI#z=zm|*>*z4C>uA)I zj|Y)cgY#csmNlC5oc5(&wI>MlcIZ?JktXmiIf+N` zXm%Q|&#D#}cMW;-s0dZRfsL_UHOkC(*xh7aYjKgV-vM0|}^(ReMuEWkEM-*qdO3|#;ABVfmc@8t#w0FR%(J`OAC8d0^A5Z6rHkp;AiM|(nOHfGv$GkI?BwJRSl8#& zudODCCGl)c#Nnah`!x5K#=X6j^UX=+Npm7aImF)kJ-LqWvo0huZZrKb+I6dTvdta2 z27(8-QQjfudb5+ulX0T`=t-HNG;^nU*%X@a9}RG50_r}r|Dbf^Oe-^cL> zp@js_gf?*47PEp&QNjNns>93 zD=yo$4UH_Z7#<(_{NnV@KR!N$P$BUOY0LhuR?Pa*QnbgKa-15sRV8mcJ)O+GEz=sM zt&fiS&GykhCR*@Y!y>t)r?%G_S{rdVI4Q&V90K@6ReEptsO8)e?G3iv-6~BAmy8)C zI{#yTCdbWT=A%kkS!RsuR&;B*VpbzkjROPo($X?omwesQNpIK4XFs<-i@VNgV6?Yg(=p1PTI6XL{z2`ONyOWTC4 zms~G4I}POe_blsQkz*Jh>c?xHcj-x+N``Y^KYEsNn5GI%gd#W>IxmzdCbwfL=DwCT2 zpAhH)z8h}tU+(XsrxTygMB)W(s`gOPO+9`3KIb=Pf)p3;toGQowm)vz)8x#G1_N(s z6HO37U{+0<+aFnu2Q5XC{`+PBYpCT%SfB7;UiKhC-(zNGWPQjt&D$z5sVS1z?OAQt zqP+6&RBu(-w6NNx+EGqWl#Q+&D1JeN&yB6%@H^qBwAWvw*$xW&P#3zsKG{ac*xc68 zFb7zxF-o)4o=IqpUX@@ozg)b%JB`Ek+pGLhrIeYTKnOEl>qq8Lv#hKx(Tum(;C%ar zT~b=SK0Qvx>$XKTj|o0@47DCMsO>eI zQzs@8Qq{^`A1!F*LmtDznOK~6Gcbx2XJLd26?XcdbmQxKWR>+gfB#%cp4WbfA-Y71 z&mM_=Ym1M&bHxKhPIVX@W#w8-%(9KX$MfeFt(w%uc1e*OdE``7)|0I%*;K}kyu9mq z#&Ol&lSz?AOC#407XZPRn0mwM61}${2Ah$d#~4Bh zPy-tqr}Bi`{%&+MH>caO_!^*~U>+hTQd+e4)jl`C0IA~2J@W&_cc}wJ5Udc)Yd5FV zQ(vb_U3{Cv*uZ0BgGp3hih8&j*V-juKBQ<(BSBZUUM6G=3KvF(hm+F*6T}8zT}0#^hWpiQ-v!BN zwTMVAi`oFHY=@PJ()F{3=}Rme#%jVH%JTko8EOvmWnVke3db8~+wVjeX(3^L>9Azh zAp18^r0hf3CV7Le`VE6d!H4P6^@qePj%(vFyngUCPoGi%q#ZI?zJD#arUuo6PcpaO zp%~GZZ~oC#^}VD19UG(9t($P}yW5<&S0;YW4(#^k@$>H4KZc_{f3pM*o5RI1t%_PR zE~n~6F4{e^p{a?Ra>7TWl!KIg>CL}vypg_Ap3&B`<3EB+!(4@b_aU+l;Xl%{So?Za zE)rzk-ZU!K{|#lT)XHL>{%%tKhnrJ>laVxWFXlMP=mZjB^*8$s7z6)Tul##gq*H|L zsLd$5-*gnk@)uHU$Ij28!$f{x`~Lw!{jY&n*i;DN+Kk9mNZQopI{`y+LP-qe{mDiS zAC|f9o;*!l;_8wvEurME4QV1a-~hO#n(DuNP0a1K_g5foCwZolQf`s8At^utBeH`V zwHdCBiEi&@Dy0qmOH{zXQzUT5#H1HCb*4Yudb-p9IzN}1n#t@8Z9u?1J#z^OqZpon zXYQq3+*h~O?aG|;rH0S-L^9nJK0Dd^O@=yEz*a;=J4gRamO5vA?qOpQ8h7-lV{N9> zv52Kas625x)yc^}%FnK@)1`hZs@hz=5>FNa7ec1cV)!wXO6CqmNLo^a2Zec57b`38 zd?l-R@1mo;`?W!rRquzH`OuHC@Ztfp%THw-3P;sWJHB9kP|S^iw0o}PzDai9y^ zb5nZJ?F`ybaYMQJ3-~Nt*R>;!mZ2B+_dkLm9gcC%9(-}SSobQejb7IM7tl(z+|jM_ zj_u5d;NY+gdT7|g%I%VvUDH?}IX^z0iNO)Am)d(36M#oR%xT#e*&-@-|M~uVf%AVX zXm+-jMbYi!EkZ0fOX_0>@GiYxil)fG#LT3>qCitjBho}P&M_t|>^z3pJ}4lBwP9$8 z-$7RVHGxmC&Ex&O##EcfuC8yZu3K2d88n<~QF;rRiN~cVg<$$L!SlLS=d~GWQ8D|1 zp`YIZ^HhVDAH`_K@Pz~urn+86`WT#pe38w}zf3mtU0gemIcbnVb|mTGGhMa`IM|iz zzO0w;H%#_#%GVxiOB!;@*&8f9J=Kxsv^H~g*5*Uib!=SvkDpv8p?PoRQ|LgF(dJ-$ zl}}8u>+WFA8ECFY&ly_S&Y`3mc++v~-q<&L(G(kE}6_9tSi>E}C2JEogVT+~L!sWDfnsJts;j@Y8srCz{jFB&8SK{I@fSG>BDKcr#!6@N0Je^KSK=_!|*=tLff& zq`bG7K8j`~Q?r?m*-bQWEsgHpDs)u-U5=^ta}+4_8)bQn<*DUM?kl(?sdi*F^7Ff} znS_}x`Nc~Av$TgztwosyW&h5Me`jd^hvfJ76!yP<*#iM)ezUoskjnG=Ucbiu>v-v6 zS2^pWfufYOwArMXGJ|xRWnfX*gnaEIMlcz{upydz?N@QH>XPx{*t+m3Y_- zTg1FmWlNiw@axO>73HR(DHI9B0tVqNHTo;;DnJ=L5`+OL%Jqfvh|B)(c`G43VHpxZ zV;Kpo`RW|H1`r&JEl%jJ#dj zRFznok@2lDN+g?IN6_y|Yr^XUIjB!n8imhu?dAttnwr#!^vIw+YpidsI3w~ z1key1T-nI_?|G~byAx}BVlq`U{HXDJ+QSJ6Xiq3A)Q8f~s^;7>8pY&$;?Bppu!@{T z65R_MB+;e1Ac-#a&qUYLQ=9!Mq+x%DLN=r~`#T9&cg9NoHkKzYgJKg-uT#F6_(Wfq z2&>zM_Pnel_x11}U%kA|2GZqp?%0~h`;tV(-!NWpOUkEac*p0y1-maQ)|Htx+m|mT zF+FF-5D`F1cD(j_FP+|*yD?qDz$XOiAh0MXnDExD#M;2QPlc4T`Ppi$3DTTs=f!W^ zrhTlZPc9CA)ESK8Jf5Z?j(c{kmFLH7pU};l$AWmLcsF%Rq0g*!RGGqPc%2e@Q}Qco zPM!0*0Th#s^$g{=)@)P9tp}_1v88Mr9J+J;A2A=#o?V|q>*_bIFIy=_NzwOas$2Pl z#>QV9)xReys$?=#!)_%=LwElC-fIGZNBKc4C>M>JkRTttIlxp3`pNhMXjV4XIj#OI zat4Wm)dmf^rAY#V*1o-N#{+w@y+#+rwn;hK{7#Q|e}@m?rJEb>XahT)%58F4)85;`>H*k^lI80JiKR}L07$O`pVvD*`77+4$XF)o{k%J2hMbFRF>PjfCST}K?--W=i3peqr^`5pSM-lRDo4AD1 zhh%q##>|)6%~w8f+ga3l{6VqOqbb+4?*Rb@6L>61!t`Udqcp)#j)5-`0PT@eQPYzE zB%QX;MsbuzhO=ubzphJkch|Eoh)M5diGJGIO+%Gnu1P`Y5ffAV`n4%v#VbG~UQAO> zcWKNuWVruG(q072`lAFD?I}l=EQ{>slZ%Yswa9d$R-6xLA-aRq!QOPYIQ`0y)3=v; zg(p82>x&4mlG>LVe>up>i-R@f($>EcGHI(j{y*ul#GNLUs4#ZP&2;&cFz1#XErHc2 zvv=>x0Fkq9+LILgZq0%hAs=HX|JgkLU#8jr{mbxcaS97x3wbYeKH{>{cbe_>wV7yE zd6l;+cJk!#Tt8KEjf(PWf{C`adsmikM|ZJ-cTHo0$QuL+|7UFDZS3<^rIg+PTo{--iYc)%wAvh#cT@(7EcWWqw%Lh3wV*XsW%>x@HkEDs-2G%4@#4Atd^gj4W7N^v`h_p2 zead$%b^e~}YApMq?6m%zoP0b|!x_M>l5aLO(JlQdL9cIRB|JQQd3UE@KB?%jwjvPC zELGR4L-uUZ*MT|Pz6Y4HCXc~xd#weo;AqviK*`R)Iz!w5D79n%bJk3f`js_1Yj6yj z=IqD9lXU6mPp|jn2vPa`_~H8cl4Qte!HObX_40Dt&!2)#kSJYGx-j!Y3n3S-UGeNO za(0wAyUNslCD}8=OVmO2u}lX?$9Zsc%Hi)E0?Zc@KZu0O&WBPh$sXivlfNfOATPhY zW{23qII43L7xiK+cVM$PIbZwoYGjmN;kAG41!tFUcGaIixgmx#KPPhimI#rwL|?(8 zRae%K)Ax#&k8zQF+xIwBbw_$bA-OZTY?ZDn&khvFYLpgPo`g_p=lTcWt@*_Mrp}Xm z#z&;aOQhb*|J88zUBmW9oEVo+WgsEdVl6F0!z>?@x78{}r;=v`c|~xwe*tn*YlhSM z8@Q6Xvg4L&opXRxfE}o;+zp~QN9LK#dyLy|u3IM0AT}o_u(YJ;EaA`VEjR9Tixq}I zo2)8lCw!aU?hw3^YY8_JLh8)PIfJxvB>ye1iDxT>wEKtI&$j~16uVgH>`3P17=w`GJL%_xG zzFTp%|M20F&15UjYBY^P$<=esVtD}WnpY0mX4LbK zHZ}kQ{K^?(9xLEu&)Ursd@>tYX-}d1*UtATIo-6mgmS_a;j=qjuEoB6h+ z2st3*_cn%YPMua98;L&)IcH{d!G3q)$Ebh+AtX_~Wq=4Gmr`|nuc~&$2~m)fAgRaj z3iDT;fuhSPBjZW1UvHhmH8Os82C@qJVY!?=cKqFA?{kMRmOVj&t2{t3A!1}ZWNl&n zFs7TyHq(iCc=1XqpTs3&QZP8b(nmEgL22s6V?>t*%H5=!qN5U+jpT;#X$Dz0nM+mv zsFVMp4F#$=T2e~SqoANANB>3gIuj*2DCpuRqmGgO!YEJ#+(5yp4V~;r{T*NcGR8#8 zDk8qz(vv9FOstVSSSv_$pdG3>?w5+JPIUy=KVJBbXFMGWx&MX_9*OSiKB#j+c7(yp zgYh{AcFDa-lDI^7n)RT43(RUsQ~gcH{MTl&v}twP({X(cFc;d{o)i=&QkS62`^N#D z1YG!1xl4wtR&1MW+NVD_I3Ol^3h5H}a8~*Yb;LTNpDJKdWL0&Aft_dGBq}ZXV^jV^ zA4qhSUZcJzcT(e3fdjf6Hl26pL;jAux${mKC@rGRoVGwy|Hlj4GDsul z1`-V?=V>ajP>J12`K&3X4fPb^+9t`m26e87&-3}lH)nEIvr0Q9@*L*+8xzH2 zx_V@7EJyKQyf~v#&Z7|L>+U`Z^nqN&{#Ms5LTwR|>yY?TaSsxoo`J{DpT{8mi*kb` zyt8>B{vlM6i?t`=me5#Y?Lr4f6L3OTyJf01!DY%xG2NaM`ZFUdU|>K7@?H|H{f8hL z_BTxPYE47=c3o?^DjN+iuN@m(G4RzG>Yi`kJ{>p(60MPvck96d9Tk;HXZnvkV^1hV zK~(0LEmeYDxK(;+3W_|*7&G7vqNC3O8w%!zxX7?(Tx(kO)24Ej7Kq_jya6DQ;4~x)y?L_&$t1lj2kR`Ytu5#omgzOQyx1fI| z!TW`p7OO_|;YU8ZJN#9(R_>;HVbAV3%4$WJ@IRoR(xxICCXCKWgGQ4q#jH%)30xAj zhhP}VP!)=o4jIcf+ZzeA!E9UQ4n>ghf{|c;kgmik>2qV_&tYFhV}`aohIb==*o`>7 zH-kih-=~gwVh!|#>uw~OVo)HVTvqbR`n))YldY%G<+dd3Ggl@MRQ>VjvG61ICxTgc zWvO{yGb36YW-IDqw&|PUCGC?=I(U{3@vI)k|16-DBI?)%87J9po*wv^O3!uw7uz7- zhYo7K?CZ1hlea;-x_ID++drA20mmb3N(5W0Jr+bWih^Q#Zh-R^nKw;G-h-~PufP{{ zRGU143-K-&s#_iim>BF&WviWxh0hx+C@B<6-?wfz-EE5E{3JI^_$UFC9sw*WgXH)j zTNDYFF4iz|Ke!4kT`3CxrQ>7aP%8<`F>EWnXtAvCI^8J_#EALkvJH}a?>7VnX@PTSYkwdPCxzd$Gn8RIYwPe`Jundh;fQN$bYJ4_uZ9pBYk{1sthA`87eSyy@BMJj z*pgt|O|p*)?fyj9GE(jo|J-BCtedfqtb7*5g;RqQ%yzhq8&SH5>w?RK+R4?{F5hi$ za|`GTd@f0g#iy~cYSO_0tlefFlwl3Dp#d^3fX7Eq70K+O?N{|RC2uueO?#JUGJ$!u&$RiX~emwg7o4H*Pkeqes`9~ zeJxxsD<{VX#ARSxuX2tVnt+g61dRWaCmN|~#`%}^2=oxv=&<-a@oaE!XhQP2av~?Y zvUv~lI-vF_y43ezOe#n^a7+FC?Uv#NN-lS2ciyC~9Dp=xA!RO6of)seLxr?HY$txT zKA5Xs?d>8rg|=2^dX&L@l>90uM;ye6hKNm>p^rKmU|z{mV~^Fvnmxv578%DCe$C}A zAfk_jwx+piOf)pgUW78as=)O=c|}c6A2M`msjdCkkteTGwe+Q4We0UV+MG&jfL6XQ z&vaURn1!dQN0L(YNf#cMi*ggK(~ZuzCQ*z%i;)$UMM0bsX5 z8j+Orgh~^#w>C&NdyMyxf&w^j(1)?G+PwMnbN>V&sL%89NlA6)FM!pIaJ+_+k`}DR zK{Ug=fx*G@-(3<(PN3v2h9d=vvF!c(_aStu(d<@=EH{%6B84y+jhiD zOG^*9ub`68F+*}hX*sV>ckx0^l9Q2$2myn~Xuu8Rfl|GB2Mt$5~&2HSU+mCnl0Jz!fF=2zmJP! zA@#r}K{KuC63NyzD|ZbQF?_RcWN3N z-viVKA16peevJr^9{VaN_zY-wnOkJ!C`L~C7{Y#8k2O_}m4Rn0maA=-%FjC6#mn4E z$0ZJCQh24Q;!1$JM?VU1HE9uVf!56q>u+oMMUV7s+Z=ija*2u_7zJB$V zDq=Bg{hrs=J9+qa*BBU@YHMrT<4~xZ$y@b|TsO0wo;5WG>tqdXNMk+z9^+YUYQJq> zxNgR&R%pSZHQMbecC5ObG;V6B$VL_$yDmLlmkMmJJZnmHqX(;IZxxyX>Yyt{wy&W< z!T{mYwT|{1prmxdMyvvRC1!cFl$%xi9+nktds52e#?XthrEpWuv?ADypv1evE%pzzZAL=dDU4gg*aeAARJm zk9KOkl;gg*=&=xA8__HMI%VZM&Q_yjK3SIF0zuVZUMEDgU%pk3gHH=_0nTdbFmwIn zXoMo!z(4ri!CjA)S>QVMgvkBig3FznL!*+JYH$u;!S(AeMg|O~q)|`K16xtkDB-%l zTL2{8^0*}9=CiULgcC$nU37^_5juhrlww&YB)Nlj-d*jisd@qHSkqGO2_{@hlDiH|Wnn&~@3{^Z+wlHoJ?XA7pRKrLR=7 zgDWf6lvJBT57k>iI0*|NR7kg1ooX#GkTkmg+$~9hL2MWFAwi$$V19(s*NPnjuCednXl>9yJEi$JRsVp8QH%UwwlC#F{9b=^_#(BKwI!&$1t zZA(ynKZo_|nwm z0zPlB-Sl{S<0yTim7&I?Ym6RvC#iqpHV)COrMhxMnxr~bOb1~Y$zUEoc;GaLanY6CA9ulbn%o=Iu?&4^{L+A3@l~+);PW8Xo=6rN-R-se zDZou^K`vRX#R2zO-IoDS+zD1%c_21n`A&MnB4}vl$+)PSzC2 z_K^cB0C7;{DK@oGyYE1CYfC%>BG?`6XY&l@Bfg}(%>x6j-Hei6x8np?$sr(dDwlCk zM&7*ji0cQb;IC-2|8Dzv1$H~)AVP3lQALF^_y*NqJ6X}yC9g07w#e0GtM#QR>f&3H zB=5Q2Tmi=dlZCUb?GkjaM)_%(X+^KdPNg^lc#D)hToRY-&OnSSMwa}Hp>pBU;F*v# zMXRQ$KEzpk*<<_HN)$7%fIr*e``t?b?12*7g74vuU0i##)Lb(OsnB_+c)=X>`gI04 z1KoJUh(HXi@}9gzGDqQ5Y?C81$F(vypa;GKnOcl!h~VV4iddcMOW-%kH6MD|_vyZm ztrwG}`f8fJBL|>quo!^4WYm$of*hoJ@m^<|qQR&GmP3bSIvHV4P!;$rfbc<~zF(I| zm&Lr>at@*SeQ6FZ<=uqu4v;AX8)|Sml92N*|B0OU;q67CF#P)9QAJT7Ke*q%pS$cZCzSO)it_#rmLYf}STQB`0SrmLc#vxS2oV4!T7eum z4nH&hq;a29zGJcMG-cmz=$p6BHX7WBz5Q1)h5VKQPR;vs=9t?qa#B_U3r7Ra&h7o#OQM z5#Q$b3`t?#pWpw)V%+(-E3>??pu22SUsB}wO+kBTRG_DN zis-0Y=hWAF2aG|6s6dlIPINfmAF_i=41&M2Cng@r;=;%g|Ngvn9I`2>1;!iy;aau3 zk)xg{-~6}Vz#a0v9{z3x^YqIX-co7->UCe%-CvaKcg2I?yc|SGMn@^MtHAX87hFxK z*ed%E^$Wyp|KN)&NSKGeqn-5ihSwUC# zu85b~M0b^s)e3KfAh4TYh<|E*_QXU-a!U@NArb;s``s-i{>mq7_}p9?476&HKcBo} z9#K61EeIh2a&a&?)KxC#GHU%3o68i!qt&FitS~R+wjUwu$SVxl5$XKEVH+8l-m#6) ziTkzW=#}~T^mcCUt7Pb!aJmDdOxCl?TZn1;AC0!c=7rTiwAz%;Wn>=8yrhMN>}%g( zStX3ofHQ`HvjNl8G|!n~LWHS~ZWrAig$8Mdv|htJ9;mQX2wYx$@n*Z#O;W7MX$33wsQX0i20z|Bw2 z|MPpXUjX#`C+Qi#oL`$34Dy};>pBcnrOF3_k)dqcEI)F5Ix8O|jZ8NY^p7_gBqWH_ zl#-Ib{gDV6!@N~J>;L@mvVPf2HaOA%W7deOhis(2uKMb2zt&R@VCse^4cnDhub6dz z(BrdzGHe?UwwCVf0j(zV;`In-4`lSj*fA;`@eRU61dM0Z{al%Js*Gkc-vJs^W^;R} zOZc+i?VodIS@(%V?zd_d{_M;+>9Wlsl27W|z1=P=2~qy}#4l2ZKP}zMv2{*&=!|y6 z>sD8qIz3t7)oKC;s;i|_y0Z3zWGpN`a>IDsp`)h1e+E3)zf3hL3Y)F(SAy_&{BO?x zvdZvg&{hw*@dE$UG#>4=wXtZ|fv!Ymx3+?8J^|&Ly``w4o1v+o@3)w1+@-er9`szl z+?&BLX7|Z{apV`*Y72>}uKR%?cm{%1SxF9~CJsaGZ z)+Ej~8))z7IB4Q_*O)hroxelnAyLs2Z_j)0R5eyrb=7-2Z{%BT;_cLRfAV4@cKT_j zgd5>}3Y*|{+S%m}!p2p}eUu(e*si#c9ScS#ySa;Mg_d`1T$Q5$6G_2X$Q!z8lK{dX zp_`&->TzT^>|9r4_WKd89|E!#2SC0|FDR^J0+fmX(#XJ<*}AtioprF+sdvyjJImOl z&L7ev2{r{}7VDccBIK>DYhY6oqJx9`<3-#7=76m`lB;m7&)hC}NQUL$nquB2M^J2H znV*UXVqbL)zrlNOJV2j5d?@%J#&te}$7!8DSuqR7A~-QBqiN-Ah5%nwRZoY@iOr9D zvJK6IzvY+&sU0M`kf~5YU|yv4htUR~>TFgW%mci|duY0d*jtkfni?^iz~Sm;;ZzM3(|B3NuW`U4o$NPOiBF$*IlXkLG=+!gd3ie!@H-9*m#)0#-r}Ie{!ZbkF>5P zEWtz~iU@Sv0q|LlRz0))20{e*#;01pwSoyx!;r7y6aWG_IT#3V+cgfPZPQa3%GR3$ zuNjBxZ5T%kbRhCAGqjV6(gy$B=(^t;W6%(JFE%}RJ|Z_k1v2iuZ)g&-7sBuCqCRTZ z1PGzd2(V%@+316IdYbBW4W@_6X7^??m1X=JMBhGxR7G}+Xnf=n!~G8INSy@Us@FgPe)<&H5?9!k zd-qE0z_EBlP)F8g17u_1<(BHwBSS<%r~m#VC?G&)?>_f0kv^q_N_dxZfLe(RcVnvv z#$AP#i>!?%cjpZr^>46;7r_)E5v#KxvwM9e7?U!eO$K@4qgG^&hrf%De+)6WsdrOs zZMb@!I2^bz zxbzqdA2afM%?Tf=EI{+fl1&N)?PX&!i7SduUJ8ks-OjEOxF2v#DkmX6zCBB`JULmohYT$@ zXpQ-L3g!4i(H|M5Tws)%qq{hiWTg8(v*d8^zhvD&W~pPV#6(04d$LEv(FV7B!;@*u zIrJVLN0lz5)gZ&w$&0`d%FheQRNq&3XrsD7gPM4uDS!-J-W3lH4ILjImX+|N-tl|q z`E7sClxDx20oDGOFVw+!E=Z|H$v{?$nlP?X&Qa0Rzj^b9b(J#p;=!974t2IO3ap1a zUM#=)^g1JBb9-E}%!71PY$(W`i-9kW%u6h(UG8QdOtPDjIvCw-!Zm=iG2;(7vFkz_(S z()U{*G8)q;4sL{UIN!6UHn~C%I_s}eu#xgaDK5BeE?L?9cr}qe@kzEgt=hwvM^uqp zehp@?G+YZuLE)-Wp>*3E6=a_U|0E6vHJhCU_)&L98Qff9Q2Em*0>CfwpR$MY&3|O7 zyU`e%0~4J``Yq7yE*yd%u%2m}<$G(hed65bt~xtAr}jA|q@s`w5irodaEel+BT5ndnlt}IfOk3VOD zgn@}ox_!|L6lZ02UyC3&SEUyU?pc4rCqxYMJhHy`utiSOseZb>O8MXka&cj*_%~;o z(6r|^r~H_aYP#*J%)Z@XPa(UN?)-wc@M`@zLc&-R60U;#tl+JK0f%>TQ1RGKy&-jj z{2w~qo-z9L5wR9fVrU0P(<51_vXF>cLy<>YbJPDHa86u%FCM=%?CFK=;n^2 zltYne9LqwO*w%DN@VRX}w=)GKNkRf3xUCGY@_m;EK)PhYBcpV|w#vn65}d{gH54)F zfv;$P!oUcSa&2IPRUVo)p*7e^?Mg24xyB$=SfoaIyM28!DJCe6jB#S;l4>c3=$dHE z$qUZq8$Y4TlUlBM)n8jHvYQqqEzXFS$`Ij(L@)%;B0XqFriiz6$OMagZAlCWjKRV8aG#oSIZ)xM;Z#hvM1 zGF!Pu9b}xGJ(#I)LZmLaKurKjo$g4R2HQ|HAr zDf>%edlw|R=6);|wF-rNZ>5~JOcAs9a|FE*>gX}>TK98LbTv^?P#lazS|!Fm7a2zQ zt}^RMIj<|2R<)xZktM*vXXnHGvE?2(_Co>*gO0$2#x)TTq67_e*oPHM1qs7UsKHzu zxDOY$u_lZ>v~y#;qY+2cKN=i7T)QiQAT1+7f4B2z$8yW@Gq^v5(o+vk8&lNuow1A4 zG9ukqY+rWAN9SSroRv$w?v)|1m>Vx(PLfc$!%%KAF;LTa^uBM-N*4jvmv`ecA1rA} z%lahkHr)cFGO5_lpd*TQnltoSzXizXLsGQkJyvoYN;)uR1j}8cgBj*qHml>5P*q@_ z@ZqTMZZ-h}1sY-1&lx!4Y?MJRkeY8(6ar@ zi}SizpZi6$k~Pa+m6XC#u$QWJ{h+}_1qm7GUyeTC!(Cx8f%&LtzC-D9c4v8^;Eee` z-TI&@^b?s_=~w#94Zm-!>$EF=-rX8-l9bjT+W}g@WzyVyRM9O{3*d{M6%3#2vFw8v z%yD(fL`uKFD(O26%|MlQ9QMgs3b zF-~ZHAVpXrn$ib+Nit11qBVsuAobHTA|k4>iuX#nAarB4656~13WjQ*{65E&yx?H`mw6s4cg4Wi#DcJ8B&_qUh|G0)S4{Z}w8Xw#3 z_r-qh(9xgqjtC4S<4xe9r2A_2DSw7;KZ+GRCZdSXMzSk&SE((rN$g(pgsiK+A0wlq zHLr3tGBFNzg1%4W)Z_hs4kBm7A5V~0SO)QS2XdCMfuSbMa~n$jZJxQ#5JFI)5h*HQ zM{7lhR^^}5COegjGouH!JWNa)e299#{Tt%61W;RmD8_B3QHa6s% z<~1m*p>EWh)mME(;0`nZFgFp{`ealm3J-fdY@UI4NR3@&H6;EmGMMCjgM*`&mc-5j z>!AK`hQF)2#C+_@Ddsr48-{*bNuIoqHdcXYhHMq+z9(?|x zp$2+-dQ;MJrxD0=>F|*kT9(?{+APDw&1B0aLuCU1hzP=zj@)JFphoK&P=HxS&2g(9 z7??0;#!=ktsVp&l)B_q&*PKc11eO2smz1MV(Uo1A<{f(+M)HMD`6cEdly2rWVEq4# zXuw7?a>A05cqx!)F>;_Y^22ps~ULm;qeHjdaDo3<{tXk7Mj8HO^zHX4N6N_VF41s0xG}dD)a&J zvmI8sm}|T{jOsj0Mmu0{&>!z*~M zQ(ak_fj6X%AHYHHi2onu8z?WEI?iWQHR|eqh+x?q9RIMs$48y*d6;X;qe>fx@fqP! zcwh$S9VHC4GfWs|vjc`%oAr_*yj`;j6!=siAwu!ICJBa`b2P5VU_|5H4N4LvYR$GBEhVM}?@BaheqcQ+tQZh&SIOsV45Hb7 zDMHev|5#V18hbOa;t;Z6eFX#83Vb(gT-(6oju2Zg7)xi2k37Bxs6(In$9v8pw*>{U zyb0SFV4!l%c3MB7-(g~)xDMcad_Zt8txqf8Gi1J;;9~ltp|WqEOudL^@pMLtrW6-f ztWTU-wXw_w_j5<8^d!5Ao?o%;ZU$ohTE(P4(r1OtwlMG5H+ zUj61EGaQ<#J{Q1RR6m(0=a@5m$36I{Uqtd<9~o;EJO+b#HSbFQw-0>b>)TYhCS+F@`0mOd*-YxZfPesP)H|O)vLA_(g@pw)ZwGMF zFVz-ACK?(VS>N|6I!M(Hn|rEpxgN0ujS00rTT<6&*=v)t2?)DLK%ft~()eHxgbJFp zZUNzSITiYP@JitRm_2TTYoOFh`sXf)hScHq4L42&5z|us`HP8^12SOh`sUmV%LN~s zt%E9hNZq2i6Z$~}4}(R1MAEmAHr5||7weoYdf3Ej>HqD=3JMC&f_^{2HQ~IN6MdM6 z>Co^P3XVs9X>zWcoBvALXnAFb;k1lpX|t^P_;nj~g}cpmLHiu3pJj z!=uEHx_V1|Dd(f1xv}v_D+9!W>4CHO*y3YePL8nXso>K8Flg$-uM5WGYwYoehd7y_ z=l3*e>(4R$@!0|OH}EJ7^5VHOCVz}e?Q^sb%CTDm@X?ui^h&#!5Pa6T_W zjOL=O%$J8cLXF{Q6+^F=3!1r<_lgDBeb+Wu3FGDBOMwuYV(278Hjra4l>hA9@aJMKADYig+)A=^otHqBOxXO}EGXr83r{(ZJ1q@bZWqR(pI~{N+Bsk9%yruA z+&@11-$tKG5vnpOGF|V-_R#*A_1sSC$kWNT`BG)K`N88b5dl_a&ovIEVZYzApOe?p zrmV(or<~1YV02i$+;zSO3wF%%B{JuU5dkmvhn$=ZNVOcQBV4`XT?T#$+DC-+)nKU7RO*X z&^pt-16R7UIeYTE79q&~X{H_MX7K;YL_ck8lvrDu;sCR8{LAlJ45Q}DvPoFNYZHT| zaZ^zeM0Ba1zATMdgfq+ZSeO$ zcR(Gahb3NPn}=guqd5u*GT+2>JM4<&cc-{+x7$^E^xVQdN%W%riD!sq;E`mUgFinQ z#NRiVfAINjLFfLE#$2ZN(GuzQ*pWSJDfOk{*R^X;<^nBnr z};77xu2rM z#4Sd7mW65)8ZYtwQb3_ToV3UKcq00U@LxWH?n0crv{>#(6d!ls{Oxx~EKf6sdoydj z<2)LF^e7Dq)%_QjyV74xC@$h1aSa_gYW&9f_RD|1#GcV8ueYx5G_HN-k>KFqm6jh> zzC`Yy;mMe+N1Cn?RG?9~Ra%VKrZbo}CT5QOxzcD)!8rd~-Fxfz`oE8OM_v1H6!k>B zdzesEEc6W#esxw>Iv1C-eN6fOE}^F8d~))4NuHjG*^F0 z3Au?S5{OC+WfObwJJX35a8TV|Dj8L?jcZS?E6idaeC8Lzal#_o;FONqeDiXlQEn#! zR>P(GJmZ9W4j$?09~~SnggY!O8(VBH@!4aco(p7k1*)Dax)hCa@h-6ImpG?%1oin= zx|2uB_Oh|T$|dchw#qpQ24O({huH zK9kt=HdV@{yz>3bq_SI~*dCtwl48W0+v5x4L^M}FjBjdRW~N$`nBDW)GeX*rX8jw6 zm|ongD)&zAHbhV)Gc$f@Fo-dwr`HfJWD(yR=M@MtlKsMM|JXN`w~_b?Z&_6Jn|NJckWhtoT))avdeborFs7!#@;%r%6i*o13dfLpI9;1TyrfoWj}Zf zJ=@}F?LT@_CK|^-@$|UpRIAJI>3pp5SerC_WbNq4EFa}fOL`}4?J?WahF3cmdyweE z8uc4K@8I3Z9r(Fob|9gThJ=(*G(*m~szp|>S=Obr7Np)sueKrDG@nvG3KX@OnM$zx z6IBn&O}n({q&8;@*S3G}WGX5edM`pT;ApjORCbRy7KHw=m=x=hUh#7?nTb?f)Vu`8 zZf$Kl-N#4$(QhlGo`#fMb*7$#9U(1#w&i2jz_)K}U~_B&O1}bZ99M%jr!w3Imn$|- zP)OLFEmvAzUb~n+R?jFKwY=t@3p%a5qfpX|7x>h4lgzE22El4UKBw2jppEC7%1$R+ zSEo?aR`sSPuXL`j?y)wZ6= z3ahqT4w;dRg}S0+c_R+0!NC`069FS)BH$U1Yw*gNZtwK%9`Pk0m@! zvo2=J%f7AX&)0Ha$qSH<$iN%krJf@9aqP7$gH4#~&C2;|4)*<@BQi_C1HKXA8jj&55mZ};;a@2(4J8i@Xo`Dl*OUqwqIWxo!wL)GlDR9oV(;L3C3uPvPa-*#O1|LFY&*Al#G1h zQH`FY5X2*hcV@rOy_?Nl6O#;;>}I4}1s87T38DV&i7JVW$y8Zldut;JjQebn-|+m$ zLlaW_7yMUHknqmD1L4WV=U9{xf-}Gl+naV5KSL8M{vuW-H z(QMzl7o?DqY}|uYQrt2(D{nm4*i-m;~_yS=VT+P!ujTWG<)+= ztlmkg(8olEMWeljLwAhu(j_8dVk(No`o}{aMm^&D#|NGupL~| zyT4DW&-j137hu$Z2SRuh@o4AOR|3}Gf9-F*OFZ)*<^jON3D`=z_ad^g(ndnUIy%Zt z+5I?hmoL$bb7U-Eizx^vBrX2Te`zk#i=ajl&5#gbp&ieYM7CSP=``~@Ghm;2Hg!^j ztd?SaDh}qcb!4H)$8X_;kz^G4A8gD%TN=!Wb3u=)uU{2AsS1f&{q;+!z3O^ClQY7u z0Vyq#rqcD#g)&SWt3M-V3E5qfjiSRU;M!l%=(*0x84MM0+o+YQ?yOvSFTNz`?S-+_ z^j~fQ@ydDghg~mEt~0-B?7ThQq8QCH<$M`p9o%;Hb*2g*eOs2S^LK6IksGhz^eVAT zNSnmZPnSj*1n8xzfScdeW(Q6jc0YWTJlVT$om9`Nt&=J#N%Yk~dL|>gW|Yi=i?+6a zIw2*c)5gr`jHriaZBx@|Gz!Wii?Wii(2dKN-&U!4?)(@>_=8Q~SNZ$X#(q-oem)7- zYjW$J82dfCDxZ?vte`D&?Be;Isy-S5IidHeEMx0zH{qTZ;dY-N&M33@87py`@o`V- zmz)<$%%fQLlGAebWzv5B3{a)M$@!_7$Ky{|0aJ?A{i>S`E6{>s5PSVPCbR^nIVOI0 zSlA6AMAOtX>hF(^5HJWc`!!U^I!{h=LuTsyO|&H%!<(DQoRBZqV$YGw31Na)^;6GP zVJQOR_5!4up;o#tlc?6|Arx7e>gFpceRCZwT9*6*GRq=<6UDUFM zjhd*mI|&Q|FI$R!9N0Oi)U%h+(Mq;8sI^mAB4n$te6;`60_=>q-=rXtxX}M!)9#Au z_M?(3n(}d(e8L>`R>eWQjTMOp_wMEl*a+FzR9JqHfANVq&wZ*nQotYb- z*n|M8H59l__uuE~Xh587*2I3>Ps^4jMkYh*-KCGD)Oe=>xc}A%^$F(qo|Z=$eo9r` zJ8~7$(LV*AB-6R9u*ZNQeW0AJ3WrB>2jvhmUp?$VIuZBm@0wB;&;=n^Xvj9_rAW^b z_d8p?2A<%%@IT2T)f!4j#|Y!(7#&IJk^heMsT9DhF=V1Yllb)<>A|pg1%&9A|KpE+ ze{H#qEiKPYV%y{Xhbtg@ls^c`Ko;p3NhR9DvnzL=1{@eT3qDk*bJX*>8zaL&ojcF{ zx1-Q!qKIhu@&)6>pjwPO4kxqQ{f+w8LD>oYn?JmJf ziV#8Or7q#v`!I0ni#E>LW)b)FI(se_~MH{Ngua|)cvi^;)Z_hEybm{&K~*Bgh0 zkwng2xoon*MqR*qg4!q!nzuQFyh?^$LTTws=tI$;?N##LzyTS@ovkg#XmPBI<{1Os zb0W8um31X0c`KHkCw^Q1_!u`CQW*@%pp`G>6O3uP5WE0M-)NkI^9<|%8ZCJd)M_!` zKOiuAYKlf1cp7++UPGK#TT=tEaPJjld|X$N{S|N}ME?ro<8K7hd{E=7GJjN-*~fbN zlDL%I=(wRdId%NmUPm8i$p0NWs!GpIeG2#it?pOZ*ix~E7~vkxKXWI$gR6jxd6vNX zGxS8(@CbX%7A2yR3o?*bF_5cX)Jsd3ndTR;p@ObQhdXzATVEc1Y8v%k^L?cmfLIDl zu%$N3oqEii@rpdc8S;X4G+r7!VE;MSMOari4oOFY+n})XO6AxXW?n6L##q*% z3yRHm>0QwEF=J%{B#!RldU}!>s+|W@c`wTLrM`Ob53&&NI$Ym+@!|y*<~rRCy<5M1 zH97vW2qf(6umFTCtgKwzQD)+S$j)NeXlDluI~f&M@D7}d3mhCAV67ZEkO&0+zPpq9 zLv_>W=sn_)R+*wB{~fG}t8M-6=8lugSI7QwX_9QXe+PCf{Xc$#c*S7tW-HI6E5)b3 zZ~cApVl1oxlr{#W|A^xvxhW#hD}>#tH_LwJy`22}s`p)}V0YmJgI4)c$M$avf%*15 z`m;qB7EYWMr1P&N()8-aNM3_zzx5d=u&ixuXegJ!-vy#gc5rjD#f9wNrj|M`yjQlv zFH45Fe{-WFzp4#fkh{Cs<8p=y`|andIgJVYCXgMQH55zF#8BhZX$zd;6rV|WlEHa*bF^wIZ2Rrja-M@&7@e;dlgCWr7hQHqH&oN!t0^FXwfePR3g4@oCjHTQhNdnM-~=z7RBlG^?LOMkjX|w;8Nb? z{5f80Uy@MtlYBVdLKMJ9X@%$5xLb;fit5?u{q*^fWn2qs3_T62I9h@`&);A}(m(}K zJ1AidPNx-Wy!(3b2evKE{}U2OZ!EWVxSpH&Z!2`Y2Y5#q36zBW>$~f*=(n$SEFgk` zZxi$-z|P3fPF%jgu#=^b4@^SLJrI;oR7z-Q;0Lj!xOz-25!C#wyJ3vZD>0rYN4MOb z>3P>aGc9{O6s7)Y_1|xK|Jyv6GO!jwsX0==bdhLpoE-&7NZSrbTArv|^AH`T#X_pw zV%^s#)tw^d8+&~r%ijxBIpBiDsGJ97RXEk}nK`ar{eH8g=RZR^3%9>QxkLHkU*trg zw(8Tf1Chkn=$`c?>-4N%o#2U7(@$kBK_z+NXuXJIVYXWLLi^8<#~4S z|IFbTlYM`rG%#%bWBlM0AmTK>^Eiz+soF2N&v66w^s_6sfFeT(k}=(D?z>I+2XWH+ zpqQLrU(fLehcU_545?fE-B;RYF+{-6+im%Qakg8s$!Q~gyO*@oqQf%P9<$T}>OoO4 zQgrp%k5@{{X~2hLXV|>=oDCbt%3*0xIw$?x;lFKV`&gOuCIvCE$Exn$ztU+uG5Pr? zetVG=?&x0l*}Zi~*j5+P^}I!I>H8#l1Q7Q$)V^cUPPnifi1SR2k%tUN()mJVFgV(= zwn_E#v;O}`yLoLKrdz&_*9zaeb0LCi=iGWA$3k1X zqT0D5fzQ2rhWeL9f7HxIgUyfc^E2TB37LNc><+fwTRgoq;51(yNEEAo_pngGs$npW zC+I6`VfRBjgzP4hcD zpGWQg&aNA+`lx7Vh%f)V_o&X}J8FsUN}LUT?CU=QP%|o+4D{ta%euA81zpe;CA0b) zIqwsIFZFf&fqS;Iq#_s;_Q8|nB1jQZw>95ERN|+s83F%zpCyZD=`extGICGl56xoz zJo=pre_^Wx9B#F`S^bzluxaUM$heww-2XPp!9?6RF>HOg;D03zImv8yO8)c5cq`oW z+Mn=zM*riFu#*&kllrV5J2mIKsG*{**4d4uM*b=BD+x{HLLg*>Xtvlh_JV;LPqnBB zH3Du4A<=T;?Oj2hGz5=ivHE#{R-9ndtrjpaCo%o-NBJZlu^X=8{lW1|2@j-AgTUfW zsCsTjE$PW3vV;;#Y$X;N(k0{`B#CXTWaMC?IJr#q+yym=NihvL$#^Lc%VC4z&Kv~) z4*Hn+(7FSH(R?w(2Aqd#>O*aLJZ;q3fYN&Kh_^}~GRMlKxg+QO>v5N2Wj?cC$$S_d zh?j_4g|;2TKGuNLsdBo=@HE>1=r~UC9AxF=`c{>R9(DDR_I7Qpj0BT>eQ$ zUU&S?ylY^4dy~Cu>elDgAFL+}S#rBKN~C;;u3W0j1F6%5A#k*v;Dbc>Cd3yX5LMH4GOuy>bSPdu$% zTn^IHb+w=D@kJ5CdR$>-Y;2Z9nk2wM2Q%S7SkVw^|M6#eP8umyP)l)p>q87-V(FKyujpO zgKHol2E2e{KqRG#cLo{Y21?3I{)U^1M!>nK&OmERVZQVMdTem`Mnj4>m-59v91c|2hn_9AvYE?|6dlI_e zAm`wgieJFOvu?QB!1GPp7!}~^wJw3`yl-SZ*LuWVPc|2%Mcw?HKRZ|ieYXd3&^|4bnSFaIui0bYST`6ilcVE>>&M1qr`%MAb%v_Q!a`e160c3CEBdiI zH@8$*lJIk_(0c02BMAT=0}cxC_SSsLmg(d4>i}=Fcj6Va~9_ZA!h}=Cpl9$|S$NO%Njs5O(pcNWEp6)i9)R1||85yD! z#1AlI?lr^;Ot+$A-jEjx5{DEq?i<7=lM=@93(l#)|(9p}t@U9Z*;Q8|PJqL={OJdt6i8+wUW+%3@%lI7m1&u`y~9J_Va zf!%Nr0Nj+|l1xTl-!8rC3$a7-F4`3FSv%Gyu|S`eu!OHbr1UxwR-!S%r8OJ^bL{Kk` zuo~-#*XXaJ@jc7)BO|<3vVeuoZFiK`mGmSALl0+VVO9F-Fd@er2URzj#bSt$stx?aw`jlS$P4$t#5Ftsi|{ z8ppAzgJm+JI%XwzK5^aW(Xq?DO&O>`%&~^U_ic~Ei;B8Ja7X^AI=6?yn@G>&5OrXt zYp`G2vZ?Gv1nxV<>k#fFK9jt349(Y&!xJ6qX!QN4_NKTy)e`Y8SG`tK<4v90RsvKb z+tzyBLxL~9<+j+{us}oO874GKzka?egJ1>dduSJzUWUKa|M=4;i2qsRBr;Q8I;O~N z+_Gy~R@?qZ4w+Pp`8u1(2UO$2hq$>{O){x8@&&snyf?oJ?FYkYuG04CPUM1;1uHB5 zgT2j#vMWM08qlH4D4(20bU0qS0gfZv(`LmV0AzIBMc_ll7CTuAdLR4Gbx{uGK4fBk z|GO>v$%6>p`ou>nl8Un&b;FXgo14bvE>7DT#muF<;pDdg6NPrmkC4Scd8!BxCQOVL z=*T`&1KX1%R{zX$SOxMKPtV$H_dN1^hraY(Xi-S~crVab%?ncTM}VIaK0}iDiD{l=FooW=->~NoJ(wczSe80I052#KKJL` z2Sa)^_h&06lJ|}ec6heH5CZf9GYsx{rMCN->4#OAe@(A+Nt4_D1ngv)<2zX$`H+rj zQ{TVQM*j^sz*bgo8kL42c>YoCoGMfP&7nv1u5N%_H&i>{CtJ3*L7ff(LbRn{#kQ70SepF=<9Hx zZ$~IudF2lYOV+6qA!LK4)!hm_Z)ZERF2(RaeDK9kIpOa zRZJ*=9t#I!CSAwzm*-c+V@Wwy(l*ki*JFYo7&IWcN=x6`sgWv4_9brhRWgBnWQlf7 zwI3=dMik*$%z0UIL~Qjh#lGf9k;?Ro@+$wVNc0lTUOue|OXlSmEuwP=NCg8~2ji=7Gxc z$L8cmmk+3%Y!3a~J9%mb53a0;G<|$(v3V(9uW*iJ{<2+}P@JiqcP9YPW3n7&#tXvL z;=#+vu`u9A5qhnH_^gI~7?%d~o8}&^KOg_J(dE%P#4R%+)27 zpJ?tJoKo+;x;UwuiR##m_cD&zjqD;>XN;h-=CeYH^2%7D4y#EtKubVh#8boaGXFEF zBZ~#mgHkSE);(MgXMe+Y+y%Dg^-q@_n7B#WZ737DJ>Z!>8G59 zVv`2~Hl3xOotMoL&1{#ke8V6ZNkhyL)B9cEY4oeRiU*I}n`kxmm}Wpuo#dl57ADT`*KW zf&01%40KV1?Ss zF3Gu1Qu5HWYyT*ej<6?Hoe2ZuAYVs#2?BG>hVOnXJ*!{&?gO*=E_e!3VJ^u7~G#aBu)**s& z0&)zg7HI3MU6_YCHW3T+?Mb|B9C}yChBIN}Tup6C22uTDk2i~LvTxu12O|uTKjEaG zFL`_GVNqGFLyV^=x+U;v9>ALH+2s*B>R;sd54J6sSDJTU% z*=|J<7qcU*j9<>8@B`6RC$ka_>4#K&rEReIM;&^{Q8C|^hCdL`U_eaxo_n4=kN5Gn zUu|6p^HA|F@>Q(7+xcA2(4yh;22W|S`_771e5O3*-8&aPxQ%{cv$L{Q%2tI+fG_KX z0*vHFc5uBE)drRuE7@&#z0Cy%-jJcUmYn-|6{&No4f|)Ak3Zk1$rtFbLNAK^vj1&h z#y~UZ!?-^<>hTfO`KcR_-T*CEw(3?z@W{F>8?HvN<-ieZN}F@ebtq=k>(fdbfTSqCNl>)5`;i1;Teqqw;5%a?nWsCtjYdL|>& zqFmyV+EQ|UI5WmRp2n#t3Q$RK# z?zAnjqAF?jZIsCBm!4`DfP;((-OkonhTt|Ilu*bZM0!r~-FU9kVC?!u*f-haMj0Od zzWpclNzgg2@Phj4*frBH9NEPp`qG8ENpL}!USmkzMJtI9q9|!rZuA{P$;V1=MEyGe zLqHG*ySpyqsL9sNXA8)v5N;NC6iVEGvRzSs3rZ1%_2Z2@u!f=sc|+U3{|q1DV3WxG zB;gYF!W^PG_F>?a-u5AJJ@_4$H<#AJW~XWk+~0;I9H>?m!MOURHZy$jTzOC+`=(B#2;c#7c z_a{2cyV>H*pZV~`@@RJ&Ia>2(sV@c6X?}mUDVHS*humFVrAKaXQokwCHz)i;{T*(P z)VmMfq77I6{@Rx%atCC7!>JvPLi`>E_4gV3Dq{}+KO&Rlw5_^lhPu$qh?5K=$lh98 zy2ygc-ClKNWrGtLp9G|y$vecP93DGA*v#@CfQ093ftR_^?QDKOcp^vb$wx&o28J_> zuw@1H?gAVx^o3AXd}}h+wkF^x1l1k8VeE{VM1`2`E|+uxi-69A+!U}nwXdvkfP`NKYfG|AF6Cx`1L)o>fug4%-}AE%6k~;j^pBHUxw@sO@6WA z=S}a!Y24_lv0?}Yr8`s%0N4KZtr=vUI*d7DUa^}j@vh^*$WoZRGqS&B8J&TJ-9J73 zZrts$3mlmCc6Bv%>gQog`~cVKdLL^(Dei^>ZT-IG%p0zwY%ya=xmU!8O^#;g%QG(# z=xfiHEjl?O1}&Re;7yp?KuHX7gHeW}yAbusIN%BPIS7CZJm1f`GS3;T7ee+`4 zof9Baz`T2`cNTjxxUh6>RWc9nrJJ}8#a@u9S#l7kfiqB1$)4Y zVX?vaYLr{C$k*+R=fIvd@eH${A0sj-jX2F?7(&P}(#r5QQnb-`nYCqm>-^(aM@otw zKO6zZZ0qIHCnukv+y?dQKOiP-sAwY)r=?!b!p&Wovww(6479bknvh(=7~y>D(K%t~ zISThchxzi77GP~?w$rcHyKSvC_+BckqaY_8`VDjs#w8GlqkD$2L&nEa;O)J)_eXe= z+=}$fHig2e0j5w$*h{NslZDW&m$Fm^?mO-BSo}_%(n0)V3eztecy1~`9;_)dR}P*0 z;gfjd>?;uq=O8E`=0(W`l~yyCqpwQmy^t{Mdm}b1eR8~kfP)Y@4dPQpAFa2u{x~^W zy%t?&ccm!}SA=a1r@Ie?B49Q*D~H|<7e!jLbF=&tt%CgnF9?s4MA-O#C)^MD!8xlQA3{B9oWCD+ z#p))-Sc4Le)jAG4Syi*~{mV;6k#Bg*d519{rgr7)dY;rY1B(Ck@|MNLzP)P=1+bnPbKtQxE<}WbnzFFFM!-XHf zQKb9iL5I(QGc@de_X2}4+4B|!>JN+Loc19}$&FAkH){|fOGKcb=MMF)c!tWB`T;-C-kKtaEST&?w>@3rmnw`m=HtDbAZO-{*xc@rz0RY9oqdaTTt)%5x#wK)=%&uy=J zNR`yfwKf<3VA^7Li~lkTXe~N=Hek*>Y$Qs3`nFq2fLB+bxEEHZsboVR51gehe*ahj zgb6P~0XSpmN3S_=Wl*Clyhux$e734#RxHY2(87>BZLUv8qYhFZaS#poiYtK{u^0dT z`4xTC(P;w>cBF;SCVXokC|5>HT>E<&UQ1-hO1UCN{OQlp+zpJZjBTwaJ@Z+M0FI-26o*eqc?Uai!1-X^u>iAR`M>$Y4OF_&A zW}iaG+-t0KLME)UBIB*)cG**Mvzay_VgDuk#MgP}PJ>TXU44Id*LEkQ_^}{M?xkWa zYAjbCZlev$H)?e5fZr?_IX5|ewlb@DS)Zt7<BFM2bI z_sV~x$+?Llf>)W}Tc1O}(Fm+~J4qNr$(J!KjC6Z(;5rvqrt@ky$h<&rq(^V+dq^^F z+8K((Ch%K39_=gJI4}Z@>ANc2!RJ_z%+#po*DLv!Kb@1inlM_N;e0p1X7SrltADQ_ zzyjI!C&wP&FKi|1RzH255=kHrWw6LR0u5gPVU|$j6u6bHg)P0*#_>G9bm>x$*og=r zjQG|wUOF4Uk)0bC{x7#Vy%7*|V9u(lB?Q6D!OZ3)5pgRkvVlXOEkm)GFOKbc8x0gT zwfGq`ry+y5{_F`veTed1b7pvAHt} z^WrJ@dlV)vpR82Z!m>fy?|>`Ecs&7_9GNvK=Rh-gjpU{7x$ZYys_&HTlGDy=qAm^| zJ>ptc!0WK<0)ZlswPPTLibA>LLQH4p*O3vUkvs@K7-UuA%#?DYf%lL!5EL*a^33tv z(sTz3w~1qtbm;m1frybd7b>DTIbYHZNqt_b=?A8@AF*dRnQ7wV?OSTEXP{IxRwbvs z`eyM)Ixem`s9?if#PK)WyW!!=%Z67Oqub8$=f|lybx$VP z^F#on^Ex&b7Ddel@^IaueQt_gnCxqrRvze$2pq^tr;iNNPxk%T7#H{a%4j3c{d2r- z$*KkZyVl(~`tcGbP#;8^yObjtjP_VPTLFIE?c*?NQkpgHX|PA#eq_RXA#CKUIsnbk zSl%puyricEOoH_v)w6yyUv14%U%^J0m(zMRs}Ib)wHSz(DLgGew_dK<8a%@G{q^NR zz1NQ?bt6yDsLtBWC&v#ZtE$^(UeZBp+348Er%@znc}Gx$-8`@w+g+d(c&?JbuA`fY z4g?louLw1KZ|lp}NorB+;55IMm@$!{0a9lHB1h+RXS}eBTII%HK0keje$J9!!-*nB z$xXWX)WZ&Gj3mw5m0I%d-2etuQ$w;G?IrTcs~Vlp%OOolke05kYy-`Y675z`V-wO0 zMarF-`vmiDfip=XQ>;O=RIRPo+gfk5rAqE%F+dqv(AlP1ZX1PR7K+y7V#l$;!L&Jv zw?qNKtiK&Z3c%iOv@vj6bOMuqFuj={n2wc##E7^Tg zw0HS3-3qmNU&RCoqD|hwKqw)R|3-)h%xL;O5`so@$!1*SYkhrKDa<718KD z6-oQ)!-PVj8&MGL4$;rRBq-u-pypXwR4V4HKaTqT=mq~^kX82npJ2AbOWNV0&)Nvs z4!^2H25Ak+Dy6`RK&o8J&`?6()kbGoEhlBuKF;Qx3wHaWy)Sc_Hg)+d?^-C#jXSdR zrZ+i1?(R<@K9X^8xM35s;pOc;0gAeq$J74JtW*cTFC-&Jr4k5OB9S8%V=c3zZJ_eq z*UG<>fPpt+$bAjl`vR#^kG_2zs&^^zWzFY6SIWsb=`tq0R~ehP{Pt!Wt-LnZ;3DPS zKXGwri-+Z7Xm5`=&V{*`H7;A zH2!+V>!2+hpaKJiIRobqQ6#HYXW6}F(ZQZGU`dP>aJvqhR_N5zPYpkT2jRn*&R(;W zEnxVUI*7)`2ElB~KRfke(@RTNh=~u|m?(3p9p^~wV+aKD^f}3xxzt!<)n2Wty(fS5 zHsZ$Z@;BkFpJ*CCw>*xn{HlJxG!XC}DsFnIwJUusIx%%bP|Vc)>(e}rHab3lUZ+NO zTStOI8DgsbmXj!je+H<*p`HX{b>tK3YwS15cg< ze_{f6j(iuJ{ZY)PL65zS#-~eta*QLzGq-Ks$|*5NGZJ0b?<8i~*V6QfDDo^4(-#F?(!2Q#1TCM?mC-YJ&^G zKcC?pz2o>evq_HKz4=ubXRI&BSoIkY&kURpTxpAJ^qeTCF$grE1iu{P z7v`XJCxK%1E&8*@5i?((1XM(>FVp_TE64}wljDxfUUyQJ@nvQcF`&AP!~-kqWTS(N zWoAy{4VMwK_Y4UOA~gosWeXa!7F0q;(B*(t1iE^H)_%EyZjNaAiLU4+*r4vW4!mey z=gEeP$Mhqy|5cl$RZt0|oWU&}P=y0R{|)IRn1A@LM1=2Y$b5QN7`e%?V%vw|HtzAQ zuPY;1)bYnhvy}YCh*Mzibvy%5q}7;n3GPjRxH2*s$7@{u+JP$mWTfw4aiFP_XN`2M zm?7auoVYa<5W0=+0k5>SLlhuQp#T~Af&Q{UD+{N)_^rk(fnyS-*(2;Zxl#=rc$KI> z|KOZ8u`&HCX6$baKMff{WA4-Oikk+x*iP5-g*cL_ze- z_|GX>VneGn2Y=Ya-9;=N%eOM)hJ4!qM7Kl=?j3O;)Ex(VF)-{w40na=?Gp&;T#?#; zzwd%N_>YfeiuZ$8#snKe&1mm&L0=kFt*JQ0yQ4mVE9`H#xX#~<;XQlS8WD{9*2-e7 z@nF?(@v*%ttu^}%>-3SWH{l}pEkt)>NFKH8GpFC(-&}}Fq`HUO*B`Bx)5zNvM-}b$ zFQP=RTV=BC^bN1$y=nxlZ2(wHS=wWjO@r0S+BHRx-Gzj3K_o(bs|LS1e$Q8H-gqM} z;flgb5i)lTMgf8{jawR{6Ynw_uq=_D|BSpaYhd>d{C^%wt-6}tmrqzW7vIX`Iw?Ez zc!@J7u8{8+C|)UlDznb?SpC&3Fw5mB*6wY-`<4>r;-o8l)|fYb7I(^nu7WQVyfoHv zyiX2RPjq3P)Zj$pu=_% zGOEv4G23}WZr|loO%=a)splxV<6c9PlG+h5V4N3^YLP1%s{SvDnG*ItWNr4JMq`cpHZ4~Kk)AAo6h}OUjC4m7$0A0 zEY0FA%1DPFFK7@Q(ikQv?V-^ zBBFhNP~Mn7rC|S$ZRjQg%>C?xwFyO|P`=>+G||BS^}JmyW`p$fh2KUxuLp zGl_}od6`CEoXW>~8b~_ZDP%h+o;;su8RpTT@Q73y{kyYo=)uKZ%eMEVSa~in{+#bWoFdvw zme8ijyRrIpX85F;P?6WXmsUh%EJ?Aw`ooBkaTTQWP>*cu4bN354gtG&XY2@l0$ zG;)|%&;%?th^Ee{Ji3Jrzb|t{BPhnu&6Te-`TL_)CZ_01WH5!MnI>+=_ebhZ-)WB^ zRYpaBMn}iDhL-$?s1JWv z5N>9o<3zq7?@t+zk>6c)E=K#^_gNYYIooE1mcoE zUqt^b{^*8tm~zBGafX0563JrKWJ-xvo2ckBg{JC_&+?jn?a!S_{VzP8w$4Ng9u}fLA+9Bl8F^iOKg2aKI5@SYCL}KI z9Z(x+JU5l28~ejCC|#Hlt`;!c!QF{yNc{>PZJ8Pv>_b4OvV$ln4ypBR%=QC7@#adS zX1qO3PlJuJ{w*kpL|YSlyeIDRYt26z==-O*;-S&W?YmlWaDr~PdoM_f_|O-q;Ch>d zmmgYhTTt!WpS-2R-u5UCYdn@4BLSGl|bM*@rN5i|Id%lr7pN8Z6qAfJE!j1817YR}ED5avNp0+GY)Mgt37&l(UuDxzZf z%4*$bWe~mqiG1;xEs(ODfg-xI2^c&)dY(EG$J@(Z#;f}k5&hpX)3A25fRW@vp zs>(Zgs(C02)pW7;duJu9?&pFY zD+96_O$9;CYA+iu+hR8Wm=El;M8{>O(S`z&wOv*WdTQ~ve7BqCKFd!??|BY*v()4E zTeTD_(R5rYJ{nh(3bm z`DzY9e*SP=6{|6Q*j|9nQrkX0T`GzPwj{46jFr#H);Zu}sXwWE6&D0L=a$>cmnOWl ztPbls-2zXJojFFr5lCMK35Nm{zDSkW|KQBv@>1X)@JQwk7*f%%iPL*ku@zl7Mf}ZZ z4o0qocJe4mo8RB+kZX?&@1?UsJS(c{q2(2e809{Id+e0=NKS;q)@TuAAliu=y)A&jxt zxx&jF+KFB!CoO#w0Gi2^nj(9NF@0aZYk?}+{%T14Z-MIfm4Y*@Ndy&1rR8cpoffB8 zYiR)m{;t5UOBU{d{bn6n@=M$8p;J80rXEih9}}qNr&Vdc%haa|JooE+<>j*@p6Y&4fnoa{X$xNBA7EW>e8XQb_8%s8n>G~ay<-l<2O z#OqD-7tKaRx&=++E)>hY_<3xh7f_OY7dA&W9;(-|_gonh<#?&9rzIL!O^>-DGO|Ek znZ`M5UnP@a-}~g!{VU&J+XiB3pd=$Bz4)xp>Eh}NRfja;4e)uinC;X(;$oKj7GW#l z*O0`&L9@){RM7)mp`_!MHZq0!1tFf6t)wik zwly3E;DG0Gy1$tZJu{^!YL%yxXH}zqDoOz*vVtFO4z#%YXhG8MLzH&MANP403%09foUc;Hx?v(ZnGlM3c`vyQx zZ+fFy0ihLXO&H)BSmb9Idl;{EYJl}Gt;CoN_55~Y?(KUV$17;;QfW$)jwCL$uxdy= zk!Je%K{+|yriuF;L_ly<)vkeNoRZS-BS4cxTMge*z#7NhA^N?&S@M>KRJh|F+D_Hy zi}$g<p&bpw)M?eSsBF{J)bWfe4) zJAtl5ukDe!PoEs)J(lvPfFg`dTF3Qt@iEZOoL7%ejFZNo58N=>U?4{zTP_yl1hO(2 zoqnxdi-pE1kCmz{^Ow+^maf{OTV)?SC>KC2D#@-p#sfU5xaEMpz6{2#(jVZe0K*+t zAIx)=O}m%^2#NI(&^E`kDcw7SfT{S%k{_m5TY_|K2m}{+)MG}Q=AT&$9Zz=Z!!Dd$ z^N)H59e_TfN@QO!+4bpyHIaPcQwJ=sFBrTN|87B@zCdno-}0b9F~IYHcfQ+NuSg#e zjhGkbS*b}%Ztia_0z6`24-y!}tkA}s_ZbQXKS?Tnl1D{P-vVA{4h=Vbi$1e<&CRDB zua-f6-3ge7mbY&p=mTawl1|V?m0T`6QnRk!5_bIfG!z<0Z^%?R z7akt&T`?wmANcW(HOMD(0oU5k`;sJd%vPd8bMR5h0p{3+W;QSp1~$HUf&ek`9hb<& zCr_PM{}a-e_Ylr(7dQ$A2FNi12&oqN%q*rQ$0}7aUgkw8&Ds9WcL@Z8tnxD%$laKt z0|;^iG+!cC+ftRvWE;r*z1P4qWoP~FP7>~RVV$vlZ@Zu(4Xt_SE8VYn&$N?_f_mKR zXY=lamoOKE7GJ-n0r4v;gPbR`ep1h033@|UkX+Fd&5-#0-k>4bBB4L?-NSHN@4JnA zcul^iNWU>1v{#gTqn2SIIJ9?LfJJAqQM;p#d*%BRujRAr=xq8d%R2z3wJxVKZ_}XQUH> zeY1S3R`xRW8R0yGTx6yhLBN;?+e2mSWaNw3RcJULu&A-%xcKxNFwwm6rlo5Xb&W^v znJo7+vrUG+`APjmy)`9TQ)DD<87P^^Wc)mY*#8;A6hbott%9c<)0!$l2@;YYG%OQi z0WY3j0#*%KId`CZ%nIFy3-K1d03)9LzI_!HDWh)?8W%P+J-tApmG(cL1Pbo{9KWrC*LhY1K$~Fk$J%^$}(qdYdl#fua#XZmKEbZ7!fAQd0O;S};GdFWQkC zg#P{{B%ez0P86hz;kG-eD75sFCmTL9P$P$aFKCnefH-Y`ecICc#s(-kf{>MX-hVuM zsZknTAOC+nx3kG5Fz5BtTCJLqMu8izgj11X(5uqg+vp|ajDYzz3;ZV*-fAT%%x(fkOJ(fXF;#7ELWDH2L!0NCWj%+NecP^YeZv0q$D!-DK!;kOfb%R~+7PAW; zR){P-hA&bJ7QNx>>B6-9hx&@@`id|AHcmP;0)mqG8j!?^L)VImkdoMUjwv$#Ew3dV z4Y=cfciheZ`-V4+gbFOqzx2Vr=>u4I%{EjRjU6~w4s{;T z;#jSpxR(Gx`IT>Z5dD<0%puf348|wVhbif{bwcNMynXKMg;{`Fri&2?Xxr^C7uOZjDsi4ov(#?*n}b&0nH4cD1-tX zgs1u#4Y$uO0z;3I><^w2UY@1nw2`X^-vieNQ6P?epmO;Ydy=6D zLXcb^Z373|mjB@J?kyF50q%lI+#^$y2oPW9*A){%HLi!-hYf8lWQN2yPfbVzs7ZB^ zG`|?miPz@MfAnA>mNC1pvI|udb;JjR>3}c=gI=*Q*ALd3oPl^-ekPE`axRedR}1dd zKekh!Y@mg9gaf_`efX+w@6y(%rx#o(PA|)c8S*G+)1X4CR}QpvS@d)j^v|%SSYr_7 ze&1wYUh79u+SJ3K2?r=3qeGY5KT1|NBnv48#%b=w=1Klr(1g#g#{W#vg#DirFRed> zqJScdusr`mrT{B>kqgn{^+VY1&m87?1$3O@zP6+xM&gChGU(xik;ZEp=#%0Ivxs8k z`4?o9{3XOaa}+uW5(IiK)rdv@f|HE+BcN}pWE=CTwj}j7c%A*(0nZQGRwKmE{o4vM z1}tl9YM>WM=?Iw`^>3qi6Z3mn>(wF*G1gd_IJrR97@xEKhRWHgA!d@Oex=Q>S8f&e zmVyj@lKJ%E5Uff=7hi*PZvh|Z{6qBi1H#w;`-iVzlTy#3Q+1!b(0l>m-eqJ||Hm$f zDsOW7T0hB!G*o^UB*|6U)Vaepo{QACuU_PSNpy|P51(E_dMfM2#bBltw&m@&V5$Aa z9;pNuUAzTgXh2FiEp2jTNzrH+1?2hmkEO4E`cCcv2)?kB)tADFdNJ?bROSK+i-kVr zcRw{41j5{Twf?VOf%vJE;at;pkacS%BV(qK>z-xf4nu1`{pkn9*_jzskn%y&Ju?_3 z>0idQdkCl!bnS2nhmoU3qci?fda+^O^9Nm>F?vBPM2{Z_P3a$52=sTOJ}GJuM0Wo3 zp2$R3uIw+D=)yJK&YJ;JiRHk4SVk4l3`UDD%X;@puFze-qp{Rzws?H_oxB$1)%9%9 z!BcI4(VJPo^%2rpiM+~{7q#K_EG?28wZQj-mVV(`Jp~~(HTGRjz5J(%w)Dd`9y7o-p&ryD}b#B(!M zEaq%!v;MQ6HsB5P)5bJv=t0+#J*#)`EhV#_f|FJ6okxYfRM+6;SsSzdTjwwx`S))S zAKcXXMfYNuH8!iZIJ1<1;4-Gg!#Q$P*=l+@!7rJ@H@?_LbPBxk1 z7ln$6yOp(VH7o2iv$@KVWeS2GsRm8Z^LGBaL5SBis=&Z;LI3|+`|7wVyKQSaH{BuK z-Q6HviXaF|cY~C4qo9P+Eea?gNF&`H0@B^xo!)CwG~P%9Uh$*SmHM zC`Z*Ve!Nutog%Tpzs`+kQ7~nG`o{+}#EzZ@4bFtmM8I5w2?FBFQ1XHA_xnEdKASE6 zq)No$^27tQc}OgnZ;^sLOa~1lc@)GCfF5&mJi18dYj{B7U_B?*0}2z8fISR^9417B z@^fIQ{=oXZCiG+fzgP1XhjUiyy{V4{xnA6&+!gU##*b&M`WVa_#JJ?-4>MQi8e8M6 zD1>Q)k*pcd{=K7On{DN^JGg^Ei-o}4Ly`Z2V*H#J<14hZP#p(_v; zF{Pb#Xj_8;h+P4zz^t!t&-AA^#TQ3VZlG<<*0NU-6UP^$!XF+=Un#1?Z&==IQ0MrQ*tRRe`E0{S*R6Gfs ze$fR?zRKX(?oK5taRv_t59N#|6;wGGp0d}IW=Vl6KqBrCeB2&+<%mq1K&!RUKB};N z{RB|@N)(y^$bvTQbrIwGyp*!m$QURATvR3I2fx;rnTRNB%sXBA>KNqSGoxc-27t&6 zz`)};vvP!DeGoX~0@M!rw)SG1ozP8|KA0@UnW$5G1^@Yp003r8c{f0v19vVpRY2v` z^c(XGeTx#ae$hQL3)k0JZWugwuJAvVxGJj2aWwIWuA3ui4-&?^?EhvcS_7D)xL7k_ zS1C$Y;h&H5J^I?%_Cqw0z{Q944xhQ=@&*5@OqT9!0eONWC7;cLOL{giUHS>=pLv29 z^7~ioAanj{IrQ}+eH1h|sjy4^#MO#~;$S8Xel|#YW|M{m^xfINC?o6BjyHb|H{bc? z8)OznCD894YwR4(zjX-`I$8WI&G=xKwfA$)?C)L^0lNrMtG~am4cLA{uAU8mz_5JZ z#8R=UyRgy)1u~%194%ej0Q<>Q)i&2SDriOfmtQO6tN3lOB`BYc#dQ8{$0|Nd#>E1K z8H@{{if15l)+UUzI(B{Ny+z~irk0C^?OWA2SKO!a;OcsYA@-+NWaB(JXfniK;>^f8 zc9)B}gj$NezeQ4_d*=M!O_M!o1is>AB@`_DM#Pwm`+(hoK=@*x7 zu}|)to>H%6y;ELyJ(0dkSz&+q^IhM#dX_!gUS0;kW<!=%!`Ni!XXQI*eOtkB2M8 z(&h$k)vId$r6`2fIz}+Xke{OJRq- zxrJKKTgl+N0@}?KZu5v#W*cBE!6F;>xY50nFM;yYrdAe2N(LZ#jGPXXl>U^{mWzzSHxQ+&pNg^~(?fC03TW#Z$Y7>hEZ z3Ha!TNrK(*LJ9{@-RC=vRFRC$cfX8Wzgqu`RoS-+Y1&S_uU{)fBxQG&EnO9w_ssO} zq%F%&if)~X?mDN9gID5sqW#QiN}?#z)j@aW_9OxEh4rM9~@gq%ldfjZKu-y5xO|sTfHzV;IfAp&$`p zonKjL^NIKSDB-@^2PXc^NYD(CneZx@F$o^S>U%}&s+cY^NLINYp!Sb}2sci5^4Gm{a1h&Hl#c937(-0kb~H%BsWJUUe9p&gk{Aw4|5U znH>2tMN|^;`p(W&(fna^$T57T`RfM{gs=KEZ*L)-okA2YP}#CIy05RiUtFKu|8zF8dAY-+>w{mv*rmk2 zobkp$KxnL+*wCrzuo9`sttt}CLJ1|M^uRu#l4Z{38@Z2OuwWk?Z;g}6R#w4z;j?8C zaVQ%Z!@68N8UfW$ki>)piJrQ(`D#~I$*UTVOhVz>SL4)v&A^xhW=aPH)Hs9>3}hyF zM}xjWZwGD#+r+&uEYux7FtOQ@qKCiX;@D|wstJXxi+Z{HPA%MA<1mO6Z_iA-cE&JF z&+-O_E3Inej1(>S$Lm+cOct9u({=ySfKPqV$)YEKo$5o@)g1l*}pe;X1PMiH)C_Ra~cMSo2iG8@Sn(|dpg*`v9Rig z>?ZJ73N-E3@?Qd&)}(*5y{Z1VnDoWm=lC1s6R1eP5@=|$%D5qj_?7x_?xSWfmEG9r zT?DCr$MNPO%^@7M$2Kp0(7ZY}4)s_h^3|ZC6~Z@^j@w`YVn6btl1dTAbrsZbGkI7a z^=C;1XRut)!H(||Am2sPXtzt&I@?d`8HB(&Fv#CED3eV>vqWI?k4~DOZ+<+WrT4DU zWqr_f&vL-1#rIefTAQm9^yq-qR%90lD+4bd9zoQZ*j%%PY#%+$enVa@DQI3^VdEhwM`3R2YCki`$6z@wDumA3Q zYnQOad8I=%tN{;~Kq3jnNYtvJ6mC)sDiNPWQoTMjJYQYOK0B|X9FG*oZ#IoK%VnCZ za;DEA`k0l~+tZT}Co$IB`&Ji6vwKAN7fs@qrQzG<_((OwVdJFoGFw!%y{(ddwr4!Z z(S(vB4moi8u>np0fZQ09LnNG{Uh~Sa_MbYEdy(YPf zi1C7atqk8lC8=UxL|DcvYV?PCdN0d&_ea#(=ADN9Fb-N0Sw&w-=j+GR5Rng1u0jehu#)gulFq1yurK7;h*N>35F%eQ(rMRj&dO>ESis z|AcsLPK{rH63hJ|ioCZZIQm}u!Bx)Ec)k+Z;S;Nd<4HCrscRtLHBoA5+;QHrrGhbr zU3+aaqTkU%Np)_?^o0m68F~!YXTE90thcAZ4eMPvNqh`fV)X;UqS#pY1H`TE$pYO| zzG5&E&eR;VwJdpQS?cE?Ee=ZE{Am+}cr9Are4&^U6T8-i2tbFyAfSef`9V)(VVrv} zwvqV=N}{U6V;%`QOL53^o`AD7zP#}T28O})@+9sm$9el%U1};X3@EdIUgeY|Kpy8S zB_*(^AhA1hBrIJpDKMMBXt@5Xqvn;Sa=K}dU~d8Y-!Nj0T2^kVy^q0d7JjskE<;$kk7W(N;UIXpW> z5|v(^FUQaL2yZ3ZqnrzNTOeo!x3q+r%jkZqzjnpwvbURo8)wsJyRu6#5K*3JJ~(xA z+AP}dENE`i!NisB%TaA=_!Own85)y5=fH)I)v4C35CBGpsB-(cX`Q(t%nf9^NpR;H>sIi8oq|}zV*=SLpi8@Qt~Cz z^4_E%cJEfLZh!_L1oPbHBMN;t%T@?PbbcDc+no{j%l%79O%8Gk&V@#pT3>?Urp|kO zn{znB7O43fU8foEgBv&ne4hgoKB}RiSt-K9?-6DlJt?mxUc6wU62IJntxN#r%CujI zv^8QC%+Z+Q1d$J3v9uDdKXWFO{Q;_G+%VhENEdoNN>G zT6Jjgcpr3{MM0ejn7}`CI6pvdO@F7*Tq?qX1b5Z~%4}UOT(Hc{%(kZ44G0BzWY{5s z!8r- z#LqAH@*tT%T*B9p0Q2gGmLY_!IDoHBQ$A3K9Jw!j7U^eGeO72Fcb}M-_C#&< zHZZfX(@kX@Z>?4?ESRDZi@1T)Ai?ob1{i8q_ zk3$vx6N{GfqN@tK=M6q(cd0E98Pt*_d)wLshVUpUDbdl+4A_xGTBZ<Px#L>`3CCJ)!u((1wl>1sc#>)H0# zQowt8q0cdfO>Kw41Ldn(_+mOG#od*mBXs$;oxJ_}YUeif;0nDt8uG_yO){Ff-N=Kn zSov-oO2OFzMfb{nS4^J6tj8eN5rWe>t{)?^$0%8)?Ds=7F&d!9t|GhhTX>8afz95g zQjgHHFw$D;sax2T<;TPq#gx3f2LL*Ze~mo{2P}GSyP_GgfBEraL|7)B&tl z0HVPwGefupv&yWBt|_Uu^UA_^q$v-+Zx|>Jrn$p8P-{SUTQ#&SA|!J!3o zXjHY;mbG&{gk1SkgHK7EVK{x8D9v#~Yf$!qs4E$9wTd+_{s-m`~8^ zz(&5Y>!>VT9+bE0l^n*Zub{nwvs5)H%D7=bcRQc_Y1%8vA0qTy|o33+Zzq^}68 zhVYkHFMVyr8%U8w8_J0cx_Daa-Dp>(2PncsGzZ5bAr7z9Q5_u z8SvHLzjKCutlsxlr^L*$P|A0uuwL6pyf-qZFw^IxUkek|g+4#bGbqwaUvoOw~>jzZu8xZ}>WZAr;$Zca+j8~VTe?&wJGF_nI4gJzEIZyP4%cP3Z$I5|ANB`6g*_yx@`UfZ55kg$f1Od7lpV z_Z)V{hg3=XuQiF3fE3UbhD46vDMl_LpaTesiBp|};bBR?ShaEc$FHxFu5OL0omV9+ z)&`!wu(UEY^~VNXOXxb4&fw#|WGP2b!ohn0Pty-g=0gOuG-;^08!avDr-eJ9=RLYo zXOZi64U=VDfiA&^WICZ&ODnw!3sk@Io>pkkGea#Wa?#HH%qm792t1BL(K{<;?UVou zrhIK8>vQqV>FV4(k;1^F@hj}_=Q_+VM;yoYw$9u0w#d+1S}Fw04ZXfNo@Qf91poud z{AoebdHwBW+#HLWt?LP?Ufz&%z!#0cwg{rFxjiO2!ML1;b>)<`*}B^05H#d~JSV3G zSQwF&KC}ls9iK~iNb+0TbJ9T|7XwXqiYUPBR3Q$dpv$v;9|jDWu(@ZgBpTF!e-=a> z0!$_iN)uv5TE|K#8Z|5t_yz`U^ENE$=+)-hENAP`k&)*aV4JGla`aB&*H2S>;32)*99hvEt00;KO%q9%hx|4r?8fiee2aSY z&aC+S`nd68o6L=-=8Fj?O9cd0i0#Atqz>h`DzgC|IzCkRH2D2zK0bDhje(U7)75ga z?@0GnlIsfVZzkgP@sXSsQMA0JLw?jMkXVKg!#iWfv)PI}%<->8(dsSBJ--g*;&BOu#yw zJ2_q=7$NG6e72;ULG%W&YVy!v4-BF~&$GRo25cLPdiacadC15U2cUR`;r?TX7 z0BRiwzGK(I0eyA1J}Cn46B0m^ULhgj$(@NZP%Kp($C`Y5GKtj^A6rmfQnKaQgPCRl(_3`h{rD703rz0$yj+;7a~DvT%7oB6*ddJ^>6xDv8*8XF6)+XxDycm6eY9 zu61?aYCR_xnNnXkvi>3bZDc7K@&c$Q)Rl@+^l8Tfnchv!-g}t9%bJxXw}9CnZf(`5 z@$FsQaNmGd%U4bA;f5pDg!pB8BT1wE2n-KDX*R!-?s&C_P|w^BmPX;L9O3iCexG#p zECY0y_bM_$K$?T1^K^d$vhDm18QuJ|xJDa;>p4_6Iy)0z2O;73Due6v?q}f&??d80 zu&jQ=UV07J+S=`a^fU}v+o53L?H8>7fqAzP-!tzBGY}Ku$F9hIN#(|Ve!pwK@^d}3 z*}71W(yt1?2MXGYYs7t1tMlU6(R!rEbZGnhkff2TH4zs-TUvZ>FRu`Vg1+Iw<%x>{ ztzPY`J^(yhj$8N4ap4XcrfQHoIO`i?%#-$(q0zyNu`xpK9cjbC(ZPnt zmi9_(_~d|?LBFso%d4}Z57k+I>s%6paB%K^2>aK8pUfn*D26O->FG%@>Gr+h-%|J} zLoq{rJ3iX-e0R~5s|fE|TYg^C?+?nPHpC1J9J6cGkcQ3ggN z`7V722i$XlP}^}Xa>IR{5CG2j%DK7Cn+Oc( z(!{(=zlroxR4UF<#Id|KnhiFf5_hu;r0737s+jRG7eWXA_N(`_mIx0s;4!TK3s(JA zk^AiI$apkhE=^#c3~rNm^;>^2k>qmOnyMUM9Rg8uTr%VZw6sz?Pw-jhr6V-w7pN!OJ9<@ z!y@FB!ga*|{odRu6Je96oLfDN-0ATB*Y{aDjriT`xUg0%YFa*=5wzL;M{| zK$MiiTIjz!5z^dZ1P$9-g=x({mVuEHd#MdC|3c#aW|e*$Ac!P?Ug!OQPW32MZ5?PR z%0bC|w6#|UO28QWYrTGYDV>$Ix6Lbi$k*Q=iV#+{wG9E#fm(oiJUqU5IcSw+ol$1=hhE`Iy@1)M!tUi4CqlY zvi`Ky;8|-Ki>R!eogK>91_<3Pi8OSwMY|`2XGx&t4=7FlgSxQ&r_N7y}(I z62kdQ)93%9`fEqsp>U^oJOO8%`@GFp=ZoWvxKPSrv!8@ulO*+MZZr=(z?ecR)CL8o z2eNw~^0H{lxJ&WATm6FYa(JVzn4Z7Zjv5pv^i;{ry@!%6gkXRrB~+iz)q zR)o)bgpdRDTq7tdTTjSgW!0C3`tY+Y8%@`grti}yld9xoYKEKM zl1kh%!sV|J=n8D-TUE~F>=BG?FA#ph40f^?E$KKP(y6luU5}nvz{O!hGbG;eX0i{b zn}u$Urw_Y>f*`Noyc&#v$wo!@H-kH#x2I^M1rxp=RJc00$8JMQXf0f#5QB(+$F27p z$f5wZe*8CXJpj2XQ`oY;apDd1N{dO71j|)UTgG>{TvXgV-cON#gV|g`iAG`J$+*}8 ztr{9QoB(i1{_iN|SZ00wB)}eXg$ALqKOxgUTtu8q@Z92ogjZH4UK>aJul#p!sEp82tECUH^|_D%x5y*x!B<1R;0 zv@3(Q?Qe#uNBchKlUb#MVaXihQ%xl+2McDSUy^1P7vGrvNg1B)6>;^od%G;nZG~wf zYQ&thQ2R-@rX!J4bQ!#F%h9iNpz@6^`67X4jV0MA{D;BOeqpjO1pk|i+20_!G%A_a z%z2GwRNGA18^49fz{r*sB*f6C<3AoyH{Po8<4Bc#v**Kp^a$D}*|NYN9|U_@6WQJj zJNrS|B!3BY-qJb4&T~v2q&vj*4-~&`pX47Ap&XnIQu;)mAuH|yCmF$+1!0WaMV(!o zpM{ZhTHpGZ-?0}~ckMf#BZVrhi$(4G(d=J-;vIeuy8px*ng7iv{^kwfj*dB`w7`5u zIPr$?9EUgX4$y7;(@|?wGpmSPcwVkC7xI)97w_#kfLdOa zH*Qc0J&8C*wzmFKs!2Ro^UZ_AmB#8(PeaiQbj!Hx2j&Ff{1)3wS~@>w$=S`M(1g{z zaxn+hC*UE$ILv=wga%57W+e?G#Q7-{(jr-2@hUpHz=XTUuDd0?!Q5Jvlb<=U5q~AbKw=G#Sm=Xy_luorK_s5$MD9%o zq`TTTK|LCtv8G@gth4J=W0#F9=~K5}s5zF7A9HLA_*=xk8H?&4IlE$o0{bLLnw9mKJ$KVLgB2@L+G|M;z|YaJX!h zTLzOCcCz$JK!pat%{4}}N^#d5biGf9|MAlDwv#@&QJ9iao2%osaXVKu0?K@(_&e&h z-zXIters~n|Fg~?&}k|mmX;=(&;TVZ>SFa)AlBLV-Oq}S zK*H-|UBr>}M?W>CxS^(xL>gQK>~_*xb7cU3VzWs9aAmCrz#$4#Pr0fUir(($M@D8r zBsU<^o>!w%3_u_m!yq{}x7mRfYLPzr!g+Ar<2;r*GOu$K(t(dw=jkSN%NE0GlUqet zKrsA754F=(8gls>7SCS~Yc}3AEjf8%bv499{6>2!_mQ-nZA?ZsPw6kqe-CZ|fV>`0 zCM%+Sbvd42A9a7L?(qgjs)^$Zn#k|s(9gi;lol5^=zv+-;Qp`=AHv0DL5CcfZ~}~u zhI)uk07R%>&?vEJZ}^KOvz9X6D(u3{x98n^4(WhyaXeAzo%;)#jjdKWlhAa7@jB#j zX8Q|_8FNvH0gt_J&iQD>oXsAH7f%@#*`Mn4?`u#Fi}-_r!>2|cu=jm$ofSZ%0evyrkHWvYhFm2L z4rt|ICH@wO7{3rDxZ$@u>tfToO~+QZ_qAoeO-^0}ECH9C1Wsx>^By!0D-b5UPM%DP>PAAq-&>y6yE#qz8hQLWRFqHX@M5LxIIocSNkk< ze0&aoBQJQpy!b2-9K60~DF9E^9qkgOnoU%xl*Ho^4KESks92Z6pawDtdQ?tg0DZ+H zkn;2MKb*2SX!RRCKJjT9Ht#{|>j;YmOJdXu_4pm1qf;^TJn*A61Hk29&!^x;aE_19 zL%{F2XU+62*rb}W*me>h^m6D18*i-std7OmBo<`LT2Hqu+j#;j@Am@JFl0*aqf$VNXqS1$riW-fIcODwdd_9HV0?(S$I(QSzUx~$!>`xTW=3>r!~U4 zlaoYOR21c-M`~YvFHd~5RK>bj5jKqMW|nrQsdH6czvsqBGd;?j2$$v3U5NW+H8DUs<|Ib zaM4#hdw76l!8O0X+95ysE*Ghai3iY?}EqpvP~*_6)_p68gIN^W%r(r_P9%1dE1TaR<(^aj~+1G%GLOXp?&Na{%wI z(ax1-;!WSR0A;8{kYfOcAJCt;d`2`|muIFO5vrw$WI<%2DE`f%vk%23N3nOyBVQwL zjTB^+lx%}``7Nk0Ri`{*+^hA0jhJtvy)pyl9zYfdIhmzrLy-LQWF7?uB07>P>oKL` zb~kBGp+bQ)qWUxHS#pW#I=;k5Ntsi&VIX#-h@HLZ#RM!~JHC+&UQU!kM z=iJs|mL$zti|@01X10%f;74TLf^^E{wht%Odk5V9L&n!$W{vm~T~IU%1Mx1=*7OVA z9q!yX*}i@=`sDf^*f2?(wIr{Ti?P6;!fUuW6<58qtQL?2RZWw#2fv=eQ|uVM=`Tpf z)$gVk39*P+T487uCT!u1$+msN5=f|gBE%RM8;9SC+X~tXlc61y?qX!v z=?mw3^&0emzAYH4eJMmeG+U_bJe7{dk?uLECXiQzxO{EM7Z)jlpb72%)p9GusF$L? z9X$Y?aeZlDO)o_XOePEf2hVrmXPM-UXHlutAF%e^s#QFT+!%oX{Ha^s^Ry<34*Fi& z<1&%0NqmuWd;U#_n>)&^6VB0bv2vPSi1AIoCBlB0SCesAo4ggmpKl*AS$$_xc^9al zrPEUkg133$$23vTEnt0O+Pi_eqhnp8kTx6XpcFKe10!)nX{e$Z#xYBJqGq!~p!-^A zgn_#`@`*OL1B|Gk2k20G^QIpWPAV2KTrJzpNaP@5zdM|&MZ`rtqkJD8%E}%B{i74^ z^=m8}B8HDk@(O4X4>T}agH3FOyN5Ca|?s# zhy4|R24ByIe$0903*~b8%y`)v!ML2#sHaGLn1$`dNJgXcK-T?MQT~&rMDygn82YAy zz5CsG(hbZa*KEdt@>hQVq*&Tsi2;gI4#2j$qKNj*UPa;Gp?hB>#X6nL-&}F518_xy zUkd~nATnU;TU*#-r?+*~t2aiOMH!k3UGL(NBEI+X^-C~?CL-druUqd%F}}Kl&7B;j zW7g*;EJ52pSbjkGWKCiq^*w$4yG+`dyM?<}M1SbH(Bdg{9Y=8!CzI+gI&k2*g--56 zKr~bB_GDkjUm}+VkOu|sKja}FJX87!&Q1&CnbG$fG`S~N zbO3KG#NtY~c1&HycNu-Tf@Y9RI3OFyM@mWl8It$ZpsP+}-ZHbvjpGS(_ea2U%(kkv z(p^bD?k7=la>7_8)H*~-`Y2Itf2X!U0YnUBHh!~|>@8uuG<+s6MCr0(mb;vX;!O^T zzXSsTc+>wMDr1QNBl+u2UjKAB{^NK*S?$ao-Gj+t^EJq7>Hw!J=uG8Y=~0r)^-##& z!4$}z!JEA1s$pei!LhNepA?$Gxi9eV!^d3;epX>+Is>**KKwiKexsaMr{(NyFWNrW zxI2=>h`xG4*FpI^4-p`LVy>AgqyydI-1Px!Ke#Qcsldtv&kAx+v7(87Q>?$fSHxiz z=o6Tnob?Smi4q)?g;tH1AKe~k)yd;E*LuoDl066d_Sfoa?j{Vfl%qpu-D=ktye=Fk zRu9Em3M&wbOIG0!F&5BQdr?z``H!Zm&}MzAL(?N_pWfWlj(Wr0zoh;|m?whT?}>GS zi39&jQo5Ylf8?%KHp%_yniK(yYb8474;xy?9<}P^&vUw zhjRx&-i~QCIM=VQ0~1NUcbGSa1OZ3hTsXUvj+bKBQc%$C%_HVZPeu%kRbcX4Ud?%} zq^+GGW)Y>N9yL1GSSuwRT3k2?ui{^P{+oYv!)wM3LqKk%$m6hW3nNqA;5RWy0qQ%W zWQ+L4T0MJRAcC=Pxb@ntvje`#b9E?398joAFEWMb5)p|J`Ht+hbT+b19#CiWBpu%q z>%$XI(AoYwLttzDf0W)Xz3-h% zjlyIhT59=joG1?)ya&?fSc_|8^FwMCgcsym=ti<^eQ=}?w#Y;Ey)~gY8}MF#*Trof zDnT5gr>F$|;jmU6_80i3;6Hh^zk3*9DnI&d*==i#7&{nUXf3HF(9d9uTPpQg88l^@B@hSMqD2z|s&BdbzZk zzH!^;eo?R)(PQG;`=}J-GJ1Hwb>eG4M*j6zcXo7iv}k$qz}lKDQPlq`OI$Gru13HPe@m_=zx?j@FS%m@b42`yQQ+mR!*% zpt9ZIX$VV(aB5X2kD&D z0@T#9b&PNvpNP~u%58PMu8-M(D{||UH1)Gp0g7?AZF?F6gH-JEN)YN$@f0m#rM)z8~l56>kLtuwB0s^$kmxh_H zt_;PrGUXD~uEaz^=LWx>m|wWks4Y!VhySV0f-#rTtXx<)`_iE^jxV*IFdJS?G=^^%w^Zao#he%SzgZjQ)~asVH??UFUBfL8kfa0e=s;)KzZ1hVmD zoK30k8Gu4DHv+_+!|S_FwU^P6o5i(_1O69KYzVhzFE|4Nbcn&zQpv9 zjm7(^Z}gQlQ`>f>(b4tMkDT(zg=sa&99ISm08U3D6}z$n3PcqGRt_#dJe~cgc1?f( zGV53>EEd1Irqp?PF^$omjST!0;aadz~cW?_$S&7P2At<#+$Mt?8Rv8xh2O!MV4U*C%T#p?loG|rnu z?m$OsdivymfL6We${ikKS&kn#A+WklG+9K10;y27XhO^ap7Tv+5sW5b{VK^ZEwnA( zoFd?!K@!Bpns3$%Ro3W$v;jbRkdFR?2i>tCBgM-g1$arnOgSSHL@Hp>cq= zB0NgbX2aUstFnLS+zezDe15B8%p^&5eMt|RcxjPeGm5+-*3!Lf4D^J*!mxHizJ8H3 zqFnTvdMbzB_fFe|gVs__1V-S)sv#a!5QDCvzIB6TEK?(poJrCNjX!D7=YjpeJcG0m ziXXy9M5JR|e83yXIf)6c3zhCFoorB@c6Q)YQTbF=^aHLWbuY;_mf7RYX!h4l!GrnyPPH24SN zPIW*I_F0mVpwkI%v*_0rUzYm(}~&LZXQA5QZ_B zvr>`?xXI&|`1tDSw|rc0D91M1XFC{lz^<(<1CBr&au}<2-n&nFBz)3(^@jpmAV_+{ zyAfD~bZr$pInhz#6RuCO;XeJDl?Tz(2y6-9fxTCMe@BQqGY&J7k2|}%P%dg!|FQ=N zuL^tu-bTbMW0}9}2iDJ2AEMaQ)Z^ad)_lpO@Wvz0cn*|0sNL&}LtHNjigw zsiflZxH;&x7vMw&P82ZRf{Yul+3jciFnCLZ0EEmxYk7xIfBiuF{xxo9P*&DM{0 zUX#qJmR8+3h{3?Nb1$@WaTQOPwAERcU6fO5;c;ibjW}@s`Zj_o==DxPzE_F0zjl-v z#k~w}Vq#L(Y1Qxh0}H7P8?5Lb9D2WzxbDq7neayY5TZmnmGx*jSy|Z=+~2Z(n08e3 z{fqJU{TRptOLYHAT=@M4d63=szkNe?#2pL+myQraN1k-(NWrs2ev3U)n1=mDoy*fg zLa`cO_=BaJq3w-dsbH3qAraS9jn0$rPIl8jpwjk7%ZrMOfd~JrIrKB{ zK#hg}tK$vV4HNjWu~J88X8>n)@#5a_Dz%dR$SnDXl>W%zWrSR07u-K2_-}=;(ke{~ zeZC5zNZjQu?q14{8jrb)I0$2b({ATUFt4=JK~QUW4hO>ck1~&CR1e^{FWN@Y_t~BK znI-phdn$2bXD4D0SiymzaO7L+Qx%nq)%4rXX#x1aLn~+?;pVf@_2Fw%FQ`ZPZ$K`j zYha{&yFi!rN?udc5g-3(Bj;-t)F&cvW#7GU`Hgv-0iZp`MjWrl3Gh4Hy1t~BeR}|d z_NTU@1sBg0LNdbt8^8`g8I;N&wvmv5l2tUw(bli-7qZ-bdhbXD8#+7NW@q0%GAv>? zd6x(3jj#S`3z3Fkvk=jm7*6eg>JUdd0X!TWP}DAGZaxWeVi~2Nl}Hj7t-)*^B#4~9 z>FDR&he{DPyA%^n?HeFt07A`j0A^gz16S0Y!3sY;hD(r=GKCpC=6H9ux4F3rh(G7& zmJ8S*i-tkGgpIqz>+VnpYDuT2#ll?BS)R-{ z44<@^@&xX?(DX`xZ4O@9Ox%jx{|S5@vh(wGltmJf`JoeGj}c$rE=Defy?nW~x1OWx zFU9kK~H)v@rHv*>Pn~*ZWdPo(q&6 zK2AXv5UQj=ufO;THH>Mg-AEWH!zm)_$>9xC=eaRV?1&C@^XBIbTy9(1G}?Q>i|^C^ zu0V36D&bCf|5f)}c?S?+7Pw7|uOy3=6yucmf`J2wE)HTD0@~(*EIiw}N$Y3qSr|O% zX*rLK1@Mjfc*8mSA6^Z>m#q*W{F<%gjCMiJrC=j_3J>Qlk&*%L5EkbPs!wFA0eIG2 z#fcw=w2empb;`ejsE%2}FKVE4bW)<@OL;GVdhE?Df#fMH!pqbB_0uL)G`p!WSl3_< zVbo`?fa(J&nxc>e!A3!r8;K3B70Bum#Q!F%FA;M4x(Kszwot$)wg3YNa9PC4G*G;c znb9jUfDz_(u^M=Tr_m}1@>cLK@n!S|LO`E?v{r#T^&DHd7Hq@$c`>kwGLmDG8b?QC zTYHjt&DIAkIxglr5L{cBInlF16kkdD`8V)+`Q>Mudmga3=cdDTvMi7hktulMF&+NB(t5)aeZk z1{*&uNq4F_2oT+g!kxce2XR)VcD8*yuobM(JV>}qKs8@92Ui?XNxJ{@gcK3zQIxQF zinkKnVf8Ku(#wQ29n8j20m^rEKOYICA$Lmp))O0>IiN_j2#UOV5*Htjg8ljkj0;{a zhFR?CRl%y*-mNERW>JActrPMhH?Mp!Px;lSrbBtshb#}Dp&^8{5 zp;YZwalO}k+A6p=ZZ6*um77an@DfPlkJV?4tH$Y(r6Gk}dJd$qJP&TcXLAmI?`IU{D<*KWfUpBw ziVbHC9wCsw(482~o@V3#K^b@t5D{Mes1}ZoGb8yzL`b{+GYjWT@2*HBc4@ShB7@5_ zLo)S|$Zx!>E+TzpztUqc)zR6R4QxkF)?nxd1A7IvEAYCj_uP5icl%5UX<*wsYHMBv z;G*<_hEYTX{*e7DOO2Syph|XPD`1w7k_KjZ8;nwA4WNFe`s_M^y}ZS$aFxTN!?e%3 zmw0bT$i6cqc$6gk6(I1Zh#w%+%7_IZEKv)3!07JM)FVC{r|;YS6=24n{qC{{qhF05K8fxl&8ep?GXw;q(r0IX{ceDV(5{z>q^GM zbUQehMtJ~ksPA7IQ@@SOmIz$GU#-7=0RJy<_?vg(&!OV diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls index cdfb6ed7fb5d..e7fefec1c732 100644 --- a/promise/etc/promise.ucls +++ b/promise/etc/promise.ucls @@ -25,7 +25,7 @@ - + @@ -67,42 +67,38 @@ - + - + - - - - - + - + + + + + - - - - - + - - - + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 3a1ecfa01b80..1315f0927aa8 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -21,19 +21,10 @@ * THE SOFTWARE. */ package com.iluwatar.promise; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -59,7 +50,12 @@ */ public class App { + private static final String URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; + private ExecutorService executor; + private CountDownLatch canStop = new CountDownLatch(2); + private App() { + executor = Executors.newFixedThreadPool(2); } /** @@ -69,67 +65,80 @@ private App() { * @throws ExecutionException if an execution error occurs. */ public static void main(String[] args) throws InterruptedException, ExecutionException { - ExecutorService executor = Executors.newSingleThreadExecutor(); + App app = new App(); try { - promiseUsage(executor); + app.run(); } finally { - executor.shutdownNow(); + app.stop(); } } - private static void promiseUsage(Executor executor) - throws InterruptedException, ExecutionException { - String urlString = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; - Promise lineCountPromise = new Promise().fulfillInAsync(() -> { - return downloadFile(urlString); - }, executor).then(fileLocation -> { - return countLines(fileLocation); - }); + private void run() throws InterruptedException, ExecutionException { + promiseUsage(); + } + + private void promiseUsage() { - Promise> charFrequencyPromise = new Promise().fulfillInAsync(() -> { - return String.valueOf(downloadFile(urlString)); - }, executor).then(fileLocation -> { - return characterFrequency(fileLocation); - }); + countLines() + .then( + count -> { + System.out.println("Line count is: " + count); + taskCompleted(); + } + ); - lineCountPromise.get(); - System.out.println("Line count is: " + lineCountPromise.get()); - charFrequencyPromise.get(); - System.out.println("Char frequency is: " + charFrequencyPromise.get()); + lowestCharFrequency() + .then( + charFrequency -> { + System.out.println("Char with lowest frequency is: " + charFrequency); + taskCompleted(); + } + ); } - private static Map characterFrequency(String fileLocation) { - // TODO Auto-generated method stub - return null; + private Promise lowestCharFrequency() { + return characterFrequency() + .then( + charFrequency -> { + return Utility.lowestFrequencyChar(charFrequency).orElse(null); + } + ); } - private static Integer countLines(String fileLocation) { - int lineCount = 0; - try (Reader reader = new FileReader(fileLocation); - BufferedReader bufferedReader = new BufferedReader(reader);) { - for (String line; (line = bufferedReader.readLine()) != null; ) { - lineCount++; - } - } catch (IOException ex) { - ex.printStackTrace(); - } - return lineCount; + private Promise> characterFrequency() { + return download(URL) + .then( + fileLocation -> { + return Utility.characterFrequency(fileLocation); + } + ); } - private static String downloadFile(String urlString) throws InterruptedException, IOException { - URL url = new URL(urlString); - File file = File.createTempFile("promise_pattern", null); - try (Reader reader = new InputStreamReader(url.openStream()); - BufferedReader bufferedReader = new BufferedReader(reader); - FileWriter writer = new FileWriter(file)) { - for (String line; (line = bufferedReader.readLine()) != null; ) { - writer.write(line); - writer.write("\n"); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - System.out.println("File downloaded at: " + file.getAbsolutePath()); - return file.getAbsolutePath(); + private Promise countLines() { + return download(URL) + .then( + fileLocation -> { + return Utility.countLines(fileLocation); + } + ); + } + + private Promise download(String urlString) { + Promise downloadPromise = new Promise() + .fulfillInAsync( + () -> { + return Utility.downloadFile(urlString); + }, executor); + + return downloadPromise; + } + + private void stop() throws InterruptedException { + canStop.await(); + executor.shutdownNow(); + } + + private void taskCompleted() { + canStop.countDown(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java new file mode 100644 index 000000000000..2cfad46d0039 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -0,0 +1,91 @@ +package com.iluwatar.promise; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.Map.Entry; + +public class Utility { + + public static Map characterFrequency(String fileLocation) { + Map characterToFrequency = new HashMap<>(); + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader);) { + for (String line; (line = bufferedReader.readLine()) != null;) { + for (char c : line.toCharArray()) { + if (!characterToFrequency.containsKey(c)) { + characterToFrequency.put(c, 1); + } else { + characterToFrequency.put(c, characterToFrequency.get(c) + 1); + } + } + } + } catch (IOException ex) { + ex.printStackTrace(); + } + return characterToFrequency; + } + + public static Optional lowestFrequencyChar(Map charFrequency) { + Optional lowestFrequencyChar = Optional.empty(); + if (charFrequency.isEmpty()) { + return lowestFrequencyChar; + } + + Iterator> iterator = charFrequency.entrySet().iterator(); + Entry entry = iterator.next(); + int minFrequency = entry.getValue(); + lowestFrequencyChar = Optional.of(entry.getKey()); + + while (iterator.hasNext()) { + entry = iterator.next(); + if (entry.getValue() < minFrequency) { + minFrequency = entry.getValue(); + lowestFrequencyChar = Optional.of(entry.getKey()); + } + } + + return lowestFrequencyChar; + } + + public static Integer countLines(String fileLocation) { + int lineCount = 0; + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader);) { + while (bufferedReader.readLine() != null) { + lineCount++; + } + } catch (IOException ex) { + ex.printStackTrace(); + } + return lineCount; + } + + public static String downloadFile(String urlString) throws MalformedURLException, IOException { + System.out.println("Downloading contents from url: " + urlString); + URL url = new URL(urlString); + File file = File.createTempFile("promise_pattern", null); + try (Reader reader = new InputStreamReader(url.openStream()); + BufferedReader bufferedReader = new BufferedReader(reader); + FileWriter writer = new FileWriter(file)) { + for (String line; (line = bufferedReader.readLine()) != null; ) { + writer.write(line); + writer.write("\n"); + } + System.out.println("File downloaded at: " + file.getAbsolutePath()); + return file.getAbsolutePath(); + } catch (IOException ex) { + throw ex; + } + } +} From f16ae08bdf5e4949bc1a169ef44244d8c4b30a74 Mon Sep 17 00:00:00 2001 From: Alexandru Somai Date: Fri, 26 Aug 2016 12:46:30 +0300 Subject: [PATCH 138/207] Remove extra space --- .../com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java index 4723257864a4..c7c75143c3aa 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java @@ -38,7 +38,7 @@ private ThreadSafeLazyLoadedIvoryTower() {} /** * The instance gets created only when it is called for first time. Lazy-loading */ - public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() { + public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() { if (instance == null) { instance = new ThreadSafeLazyLoadedIvoryTower(); From 095adda7e925e4d3925e9f6fe7d61b8018fc8d1b Mon Sep 17 00:00:00 2001 From: Alexandru Somai Date: Fri, 26 Aug 2016 12:54:20 +0300 Subject: [PATCH 139/207] Change access level to private --- .../com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 0c450c60c10d..6536978fc906 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -53,7 +53,7 @@ public static InitializingOnDemandHolderIdiom getInstance() { * Provides the lazy-loaded Singleton instance. */ private static class HelperHolder { - public static final InitializingOnDemandHolderIdiom INSTANCE = + private static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); } } From 483c61a82af39ba7fc9c226bf112dc8db511e429 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 00:16:36 +0530 Subject: [PATCH 140/207] Some refactoring, added javadocs --- .../main/java/com/iluwatar/promise/App.java | 133 +++++++++++------- .../java/com/iluwatar/promise/Promise.java | 36 +++-- .../java/com/iluwatar/promise/Utility.java | 46 +++--- .../com/iluwatar/promise/PromiseTest.java | 46 ++++-- 4 files changed, 171 insertions(+), 90 deletions(-) diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 1315f0927aa8..2b2ae78b4b8d 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -29,35 +29,45 @@ import java.util.concurrent.Executors; /** - * - *

    The Promise object is used for asynchronous computations. A Promise represents an operation that - * hasn't completed yet, but is expected in the future. - * - *

    A Promise represents a proxy for a value not necessarily known when the promise is created. It - * allows you to associate dependent promises to an asynchronous action's eventual success value or - * failure reason. This lets asynchronous methods return values like synchronous methods: instead of the final - * value, the asynchronous method returns a promise of having a value at some point in the future. - * + * + * The Promise object is used for asynchronous computations. A Promise represents an operation + * that hasn't completed yet, but is expected in the future. + * + *

    A Promise represents a proxy for a value not necessarily known when the promise is created. It + * allows you to associate dependent promises to an asynchronous action's eventual success value or + * failure reason. This lets asynchronous methods return values like synchronous methods: instead + * of the final value, the asynchronous method returns a promise of having a value at some point + * in the future. + * *

    Promises provide a few advantages over callback objects: *

    - * + * *

    + * In this application the usage of promise is demonstrated with two examples: + *

      + *
    • Count Lines: In this example a file is downloaded and its line count is calculated. + * The calculated line count is then consumed and printed on console. + *
    • Lowest Character Frequency: In this example a file is downloaded and its lowest frequency + * character is found and printed on console. This happens via a chain of promises, we start with + * a file download promise, then a promise of character frequency, then a promise of lowest frequency + * character which is finally consumed and result is printed on console. + *
    * * @see CompletableFuture */ public class App { - private static final String URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; + private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; private ExecutorService executor; - private CountDownLatch canStop = new CountDownLatch(2); - + private CountDownLatch stopLatch = new CountDownLatch(2); + private App() { executor = Executors.newFixedThreadPool(2); } - + /** * Program entry point * @param args arguments @@ -67,78 +77,99 @@ private App() { public static void main(String[] args) throws InterruptedException, ExecutionException { App app = new App(); try { - app.run(); + app.promiseUsage(); } finally { app.stop(); } } - private void run() throws InterruptedException, ExecutionException { - promiseUsage(); + private void promiseUsage() { + calculateLineCount(); + + calculateLowestFrequencyChar(); } - private void promiseUsage() { - - countLines() - .then( - count -> { - System.out.println("Line count is: " + count); + /* + * Calculate the lowest frequency character and when that promise is fulfilled, + * consume the result in a Consumer + */ + private void calculateLowestFrequencyChar() { + lowestFrequencyChar() + .thenAccept( + charFrequency -> { + System.out.println("Char with lowest frequency is: " + charFrequency); taskCompleted(); } ); - - lowestCharFrequency() - .then( - charFrequency -> { - System.out.println("Char with lowest frequency is: " + charFrequency); + } + + /* + * Calculate the line count and when that promise is fulfilled, consume the result + * in a Consumer + */ + private void calculateLineCount() { + countLines() + .thenAccept( + count -> { + System.out.println("Line count is: " + count); taskCompleted(); } ); } - private Promise lowestCharFrequency() { + /* + * Calculate the character frequency of a file and when that promise is fulfilled, + * then promise to apply function to calculate lowest character frequency. + */ + private Promise lowestFrequencyChar() { return characterFrequency() - .then( - charFrequency -> { - return Utility.lowestFrequencyChar(charFrequency).orElse(null); - } - ); + .thenApply(Utility::lowestFrequencyChar); } + /* + * Download the file at DEFAULT_URL and when that promise is fulfilled, + * then promise to apply function to calculate character frequency. + */ private Promise> characterFrequency() { - return download(URL) - .then( - fileLocation -> { - return Utility.characterFrequency(fileLocation); - } - ); + return download(DEFAULT_URL) + .thenApply(Utility::characterFrequency); } + /* + * Download the file at DEFAULT_URL and when that promise is fulfilled, + * then promise to apply function to count lines in that file. + */ private Promise countLines() { - return download(URL) - .then( - fileLocation -> { - return Utility.countLines(fileLocation); - } - ); + return download(DEFAULT_URL) + .thenApply(Utility::countLines); } + /* + * Return a promise to provide the local absolute path of the file downloaded in background. + * This is an async method and does not wait until the file is downloaded. + */ private Promise download(String urlString) { Promise downloadPromise = new Promise() .fulfillInAsync( () -> { return Utility.downloadFile(urlString); - }, executor); - + }, executor) + .onError( + throwable -> { + throwable.printStackTrace(); + taskCompleted(); + } + ); + return downloadPromise; } private void stop() throws InterruptedException { - canStop.await(); + stopLatch.await(); executor.shutdownNow(); } - + private void taskCompleted() { - canStop.countDown(); + stopLatch.countDown(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 7d8a97e844e6..870e1556d310 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -36,6 +36,7 @@ public class Promise extends PromiseSupport { private Runnable fulfillmentAction; + private Consumer exceptionHandler; /** * Creates a promise that will be fulfilled in future. @@ -61,9 +62,17 @@ public void fulfill(T value) { @Override public void fulfillExceptionally(Exception exception) { super.fulfillExceptionally(exception); + handleException(exception); postFulfillment(); } + private void handleException(Exception exception) { + if (exceptionHandler == null) { + return; + } + exceptionHandler.accept(exception); + } + private void postFulfillment() { if (fulfillmentAction == null) { return; @@ -83,8 +92,8 @@ public Promise fulfillInAsync(final Callable task, Executor executor) { executor.execute(() -> { try { fulfill(task.call()); - } catch (Exception e) { - fulfillExceptionally(e); + } catch (Exception ex) { + fulfillExceptionally(ex); } }); return this; @@ -96,11 +105,22 @@ public Promise fulfillInAsync(final Callable task, Executor executor) { * @param action action to be executed. * @return a new promise. */ - public Promise then(Consumer action) { + public Promise thenAccept(Consumer action) { Promise dest = new Promise<>(); fulfillmentAction = new ConsumeAction(this, dest, action); return dest; } + + /** + * Set the exception handler on this promise. + * @param exceptionHandler a consumer that will handle the exception occurred while fulfilling + * the promise. + * @return this + */ + public Promise onError(Consumer exceptionHandler) { + this.exceptionHandler = exceptionHandler; + return this; + } /** * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with @@ -108,7 +128,7 @@ public Promise then(Consumer action) { * @param func function to be executed. * @return a new promise. */ - public Promise then(Function func) { + public Promise thenApply(Function func) { Promise dest = new Promise<>(); fulfillmentAction = new TransformAction(this, dest, func); return dest; @@ -135,8 +155,8 @@ public void run() { try { action.accept(src.get()); dest.fulfill(null); - } catch (Throwable e) { - dest.fulfillExceptionally((Exception) e.getCause()); + } catch (Throwable throwable) { + dest.fulfillExceptionally((Exception) throwable.getCause()); } } } @@ -162,8 +182,8 @@ public void run() { try { V result = func.apply(src.get()); dest.fulfill(result); - } catch (Throwable e) { - dest.fulfillExceptionally((Exception) e.getCause()); + } catch (Throwable throwable) { + dest.fulfillExceptionally((Exception) throwable.getCause()); } } } diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java index 2cfad46d0039..8d5be2538f4f 100644 --- a/promise/src/main/java/com/iluwatar/promise/Utility.java +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -12,15 +12,19 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Optional; import java.util.Map.Entry; public class Utility { + /** + * Calculates character frequency of the file provided. + * @param fileLocation location of the file. + * @return a map of character to its frequency, an empty map if file does not exist. + */ public static Map characterFrequency(String fileLocation) { Map characterToFrequency = new HashMap<>(); - try (Reader reader = new FileReader(fileLocation); - BufferedReader bufferedReader = new BufferedReader(reader);) { + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader)) { for (String line; (line = bufferedReader.readLine()) != null;) { for (char c : line.toCharArray()) { if (!characterToFrequency.containsKey(c)) { @@ -35,33 +39,35 @@ public static Map characterFrequency(String fileLocation) { } return characterToFrequency; } - - public static Optional lowestFrequencyChar(Map charFrequency) { - Optional lowestFrequencyChar = Optional.empty(); - if (charFrequency.isEmpty()) { - return lowestFrequencyChar; - } - + + /** + * @return the character with lowest frequency if it exists, {@code Optional.empty()} otherwise. + */ + public static Character lowestFrequencyChar(Map charFrequency) { + Character lowestFrequencyChar = null; Iterator> iterator = charFrequency.entrySet().iterator(); Entry entry = iterator.next(); int minFrequency = entry.getValue(); - lowestFrequencyChar = Optional.of(entry.getKey()); - + lowestFrequencyChar = entry.getKey(); + while (iterator.hasNext()) { entry = iterator.next(); if (entry.getValue() < minFrequency) { minFrequency = entry.getValue(); - lowestFrequencyChar = Optional.of(entry.getKey()); + lowestFrequencyChar = entry.getKey(); } } - + return lowestFrequencyChar; } - + + /** + * @return number of lines in the file at provided location. 0 if file does not exist. + */ public static Integer countLines(String fileLocation) { int lineCount = 0; - try (Reader reader = new FileReader(fileLocation); - BufferedReader bufferedReader = new BufferedReader(reader);) { + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader)) { while (bufferedReader.readLine() != null) { lineCount++; } @@ -71,11 +77,15 @@ public static Integer countLines(String fileLocation) { return lineCount; } + /** + * Downloads the contents from the given urlString, and stores it in a temporary directory. + * @return the absolute path of the file downloaded. + */ public static String downloadFile(String urlString) throws MalformedURLException, IOException { System.out.println("Downloading contents from url: " + urlString); URL url = new URL(urlString); File file = File.createTempFile("promise_pattern", null); - try (Reader reader = new InputStreamReader(url.openStream()); + try (Reader reader = new InputStreamReader(url.openStream()); BufferedReader bufferedReader = new BufferedReader(reader); FileWriter writer = new FileWriter(file)) { for (String line; (line = bufferedReader.readLine()) != null; ) { diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index de0ecb6d73f8..45c4c1d368f6 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -26,6 +26,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -40,7 +43,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - /** * Tests Promise class. */ @@ -73,7 +75,8 @@ public void promiseIsFulfilledWithAnExceptionIfTaskThrowsAnException() testWaitingSomeTimeForPromiseToBeFulfilled(); } - private void testWaitingForeverForPromiseToBeFulfilled() throws InterruptedException, TimeoutException { + private void testWaitingForeverForPromiseToBeFulfilled() + throws InterruptedException, TimeoutException { Promise promise = new Promise<>(); promise.fulfillInAsync(new Callable() { @@ -134,7 +137,7 @@ public void dependentPromiseIsFulfilledAfterTheConsumerConsumesTheResultOfThisPr throws InterruptedException, ExecutionException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(value -> { + .thenAccept(value -> { assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); }); @@ -149,17 +152,18 @@ public void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnExceptio throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(new Consumer() { + .thenAccept(new Consumer() { @Override - public void accept(Integer t) { + public void accept(Integer value) { throw new RuntimeException("Barf!"); } }); try { dependentPromise.get(); - fail("Fetching dependent promise should result in exception if the action threw an exception"); + fail("Fetching dependent promise should result in exception " + + "if the action threw an exception"); } catch (ExecutionException ex) { assertTrue(promise.isDone()); assertFalse(promise.isCancelled()); @@ -167,7 +171,8 @@ public void accept(Integer t) { try { dependentPromise.get(1000, TimeUnit.SECONDS); - fail("Fetching dependent promise should result in exception if the action threw an exception"); + fail("Fetching dependent promise should result in exception " + + "if the action threw an exception"); } catch (ExecutionException ex) { assertTrue(promise.isDone()); assertFalse(promise.isCancelled()); @@ -179,7 +184,7 @@ public void dependentPromiseIsFulfilledAfterTheFunctionTransformsTheResultOfThis throws InterruptedException, ExecutionException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(value -> { + .thenApply(value -> { assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); return String.valueOf(value); }); @@ -195,17 +200,18 @@ public void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsExcepti throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(new Function() { + .thenApply(new Function() { @Override - public String apply(Integer t) { + public String apply(Integer value) { throw new RuntimeException("Barf!"); } }); try { dependentPromise.get(); - fail("Fetching dependent promise should result in exception if the function threw an exception"); + fail("Fetching dependent promise should result in exception " + + "if the function threw an exception"); } catch (ExecutionException ex) { assertTrue(promise.isDone()); assertFalse(promise.isCancelled()); @@ -213,7 +219,8 @@ public String apply(Integer t) { try { dependentPromise.get(1000, TimeUnit.SECONDS); - fail("Fetching dependent promise should result in exception if the function threw an exception"); + fail("Fetching dependent promise should result in exception " + + "if the function threw an exception"); } catch (ExecutionException ex) { assertTrue(promise.isDone()); assertFalse(promise.isCancelled()); @@ -228,6 +235,19 @@ public void fetchingAnAlreadyFulfilledPromiseReturnsTheFulfilledValueImmediately promise.get(1000, TimeUnit.SECONDS); } + + @SuppressWarnings("unchecked") + @Test + public void exceptionHandlerIsCalledWhenPromiseIsFulfilledExceptionally() { + Promise promise = new Promise<>(); + Consumer exceptionHandler = mock(Consumer.class); + promise.onError(exceptionHandler); + + Exception exception = new Exception("barf!"); + promise.fulfillExceptionally(exception); + + verify(exceptionHandler).accept(eq(exception)); + } private static class NumberCrunchingTask implements Callable { @@ -236,7 +256,7 @@ private static class NumberCrunchingTask implements Callable { @Override public Integer call() throws Exception { // Do number crunching - Thread.sleep(1000); + Thread.sleep(100); return CRUNCHED_NUMBER; } } From 5796e1967f85cd276d1b5fb311b3f1007bbcb8ae Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 11:50:33 +0530 Subject: [PATCH 141/207] Work on #403, updated diagram and finishing touches --- promise/etc/promise.png | Bin 55725 -> 59210 bytes promise/etc/promise.ucls | 38 +++++++++--------- .../main/java/com/iluwatar/promise/App.java | 5 ++- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/promise/etc/promise.png b/promise/etc/promise.png index 0aef198acb4e9abdb4cf5b34f61869b46e870c8a..cdb43eb6b49a11b317d7c8abbb8ef3fd43ec3779 100644 GIT binary patch literal 59210 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 B1!4dI diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls index e7fefec1c732..79a11e7d5012 100644 --- a/promise/etc/promise.ucls +++ b/promise/etc/promise.ucls @@ -31,7 +31,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -67,41 +67,41 @@ - + - - - - - - - - - + - + - + - + - + - + + + + + + + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 2b2ae78b4b8d..672c20bfa969 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -61,11 +61,12 @@ public class App { private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; - private ExecutorService executor; - private CountDownLatch stopLatch = new CountDownLatch(2); + private final ExecutorService executor; + private final CountDownLatch stopLatch; private App() { executor = Executors.newFixedThreadPool(2); + stopLatch = new CountDownLatch(2); } /** From ad11ea46b11e638ee564b86eaf2e5763a3f780f1 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 11:55:30 +0530 Subject: [PATCH 142/207] Work on #403, javadocs updated --- promise/src/main/java/com/iluwatar/promise/Promise.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 870e1556d310..3165142faae0 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -29,7 +29,11 @@ import java.util.function.Function; /** - * Implements the promise pattern. + * A Promise represents a proxy for a value not necessarily known when the promise is created. It + * allows you to associate dependent promises to an asynchronous action's eventual success value or + * failure reason. This lets asynchronous methods return values like synchronous methods: instead + * of the final value, the asynchronous method returns a promise of having a value at some point + * in the future. * * @param type of result. */ From 59cf1003021f32b3e51dfaa27b7f50fb17f2c8ba Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 12:04:24 +0530 Subject: [PATCH 143/207] #403, updated javadocs --- promise/src/main/java/com/iluwatar/promise/Promise.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 3165142faae0..e7e56837bc1a 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -139,7 +139,7 @@ public Promise thenApply(Function func) { } /** - * A consume action provides the action, the value from source promise and fulfills the + * Accesses the value from source promise and calls the consumer, then fulfills the * destination promise. */ private class ConsumeAction implements Runnable { @@ -166,8 +166,8 @@ public void run() { } /** - * A function action provides transformation function, value from source promise and fulfills the - * destination promise with the transformed value. + * Accesses the value from source promise, then fulfills the destination promise using the + * transformed value. The source value is transformed using the transformation function. */ private class TransformAction implements Runnable { @@ -184,8 +184,7 @@ private TransformAction(Promise src, Promise dest, Function @Override public void run() { try { - V result = func.apply(src.get()); - dest.fulfill(result); + dest.fulfill(func.apply(src.get())); } catch (Throwable throwable) { dest.fulfillExceptionally((Exception) throwable.getCause()); } From e73867f9a15132a8160a84eddc8da4ff5882c190 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 13:24:53 +0200 Subject: [PATCH 144/207] Work on #190: Add automagic puml generation in pom.xml's --- aggregator-microservices/pom.xml | 22 +++++++++++++++++++++- api-gateway/pom.xml | 22 +++++++++++++++++++++- naked-objects/pom.xml | 19 ++++++++++++++++++- pom.xml | 27 ++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index 0133e9ea486d..6e9496ba3c10 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -35,9 +35,29 @@ 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 - \ No newline at end of file + diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 471ffda7db34..48bfff9c3ca2 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -35,9 +35,29 @@ 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 - \ No newline at end of file + diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index b3e48dcb69bc..d416f2a7219e 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -317,6 +317,23 @@ org.apache.maven.plugins maven-surefire-report-plugin + + com.github.markusmo3.urm + urm-maven-plugin + ${urm.version} + + ${project.basedir}/../etc + + com.iluwatar + domainapp + + + + naked-objects + naked-objects-webapp + + + @@ -387,4 +404,4 @@ integtests webapp - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 6f3e0d69896f..80483b905f70 100644 --- a/pom.xml +++ b/pom.xml @@ -45,8 +45,9 @@ 1.15.1 1.10.19 4.12.1 - 4.5.2 + 4.5.2 2.22 + 1.4.0 abstract-factory @@ -422,6 +423,30 @@ + + + com.github.markusmo3.urm + urm-maven-plugin + ${urm.version} + + + process-classes + + map + + + + + ${project.basedir}/etc + + com.iluwatar + + + + java-design-patterns + + + From 36fe2499603cd103f20ffd3be9be583268415917 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 13:29:12 +0200 Subject: [PATCH 145/207] Work on #190: Add first batch of automagically generated puml files --- .../etc/abstract-document.urm.puml | 59 ++++++ .../etc/abstract-factory.urm.puml | 88 +++++++++ adapter/etc/adapter.urm.puml | 35 ++++ .../etc/aggregator-service.urm.puml | 41 ++++ .../etc/information-microservice.urm.puml | 12 ++ .../etc/inventory-microservice.urm.puml | 12 ++ api-gateway/etc/api-gateway-service.urm.puml | 48 +++++ api-gateway/etc/image-microservice.urm.puml | 12 ++ api-gateway/etc/price-microservice.urm.puml | 12 ++ .../etc/async-method-invocation.urm.puml | 50 +++++ bridge/etc/bridge.urm.puml | 89 +++++++++ builder/etc/builder.urm.puml | 100 ++++++++++ .../etc/business-delegate.urm.puml | 55 ++++++ caching/etc/caching.urm.puml | 100 ++++++++++ callback/etc/callback.urm.puml | 25 +++ chain/etc/chain.urm.puml | 60 ++++++ command/etc/command.urm.puml | 84 ++++++++ composite/etc/composite.urm.puml | 42 ++++ dao/etc/dao.urm.puml | 65 +++++++ data-mapper/etc/data-mapper.urm.puml | 42 ++++ decorator/etc/decorator.urm.puml | 29 +++ delegation/etc/delegation.urm.puml | 36 ++++ .../etc/dependency-injection.urm.puml | 48 +++++ .../etc/double-checked-locking.urm.puml | 20 ++ double-dispatch/etc/double-dispatch.urm.puml | 65 +++++++ .../etc/event-aggregator.urm.puml | 73 +++++++ .../etc/event-driven-architecture.urm.puml | 62 ++++++ execute-around/etc/execute-around.urm.puml | 14 ++ facade/etc/facade.urm.puml | 57 ++++++ factory-kit/etc/factory-kit.urm.puml | 45 +++++ factory-method/etc/factory-method.urm.puml | 53 +++++ feature-toggle/etc/feature-toggle.urm.puml | 47 +++++ fluentinterface/etc/fluentinterface.urm.puml | 71 +++++++ flux/etc/flux.urm.puml | 115 +++++++++++ flyweight/etc/flyweight.urm.puml | 60 ++++++ .../etc/front-controller.urm.puml | 50 +++++ .../etc/half-sync-half-async.urm.puml | 30 +++ hexagonal/etc/hexagonal.urm.puml | 183 ++++++++++++++++++ .../etc/intercepting-filter.urm.puml | 88 +++++++++ interpreter/etc/interpreter.urm.puml | 50 +++++ iterator/etc/iterator.urm.puml | 48 +++++ layers/etc/layers.urm.puml | 125 ++++++++++++ lazy-loading/etc/lazy-loading.urm.puml | 35 ++++ mediator/etc/mediator.urm.puml | 68 +++++++ memento/etc/memento.urm.puml | 48 +++++ message-channel/etc/message-channel.urm.puml | 8 + .../etc/model-view-controller.urm.puml | 69 +++++++ .../etc/model-view-presenter.urm.puml | 87 +++++++++ monad/etc/monad.urm.puml | 35 ++++ monostate/etc/monostate.urm.puml | 32 +++ multiton/etc/multiton.urm.puml | 29 +++ mute-idiom/etc/mute-idiom.urm.puml | 23 +++ mutex/etc/mutex.urm.puml | 27 +++ naked-objects/etc/naked-objects-dom.urm.puml | 84 ++++++++ .../etc/naked-objects-fixture.urm.puml | 92 +++++++++ .../etc/naked-objects-integtests.urm.puml | 92 +++++++++ null-object/etc/null-object.urm.puml | 40 ++++ object-pool/etc/object-pool.urm.puml | 29 +++ observer/etc/observer.urm.puml | 73 +++++++ page-object/etc/page-object.urm.puml | 8 + poison-pill/etc/poison-pill.urm.puml | 72 +++++++ .../etc/private-class-data.urm.puml | 34 ++++ .../etc/producer-consumer.urm.puml | 37 ++++ property/etc/property.urm.puml | 54 ++++++ prototype/etc/prototype.urm.puml | 81 ++++++++ proxy/etc/proxy.urm.puml | 24 +++ .../etc/publish-subscribe.urm.puml | 8 + reactor/etc/reactor.urm.puml | 151 +++++++++++++++ .../etc/reader-writer-lock.urm.puml | 58 ++++++ repository/etc/repository.urm.puml | 56 ++++++ ...rce-acquisition-is-initialization.urm.puml | 16 ++ semaphore/etc/semaphore.urm.puml | 58 ++++++ servant/etc/servant.urm.puml | 55 ++++++ service-layer/etc/service-layer.urm.puml | 158 +++++++++++++++ service-locator/etc/service-locator.urm.puml | 38 ++++ singleton/etc/singleton.urm.puml | 42 ++++ specification/etc/specification.urm.puml | 106 ++++++++++ state/etc/state.urm.puml | 37 ++++ step-builder/etc/step-builder.urm.puml | 91 +++++++++ strategy/etc/strategy.urm.puml | 33 ++++ template-method/etc/template-method.urm.puml | 36 ++++ thread-pool/etc/thread-pool.urm.puml | 35 ++++ tolerant-reader/etc/tolerant-reader.urm.puml | 38 ++++ twin/etc/twin.urm.puml | 25 +++ value-object/etc/value-object.urm.puml | 21 ++ visitor/etc/visitor.urm.puml | 57 ++++++ 86 files changed, 4700 insertions(+) create mode 100644 abstract-document/etc/abstract-document.urm.puml create mode 100644 abstract-factory/etc/abstract-factory.urm.puml create mode 100644 adapter/etc/adapter.urm.puml create mode 100644 aggregator-microservices/etc/aggregator-service.urm.puml create mode 100644 aggregator-microservices/etc/information-microservice.urm.puml create mode 100644 aggregator-microservices/etc/inventory-microservice.urm.puml create mode 100644 api-gateway/etc/api-gateway-service.urm.puml create mode 100644 api-gateway/etc/image-microservice.urm.puml create mode 100644 api-gateway/etc/price-microservice.urm.puml create mode 100644 async-method-invocation/etc/async-method-invocation.urm.puml create mode 100644 bridge/etc/bridge.urm.puml create mode 100644 builder/etc/builder.urm.puml create mode 100644 business-delegate/etc/business-delegate.urm.puml create mode 100644 caching/etc/caching.urm.puml create mode 100644 callback/etc/callback.urm.puml create mode 100644 chain/etc/chain.urm.puml create mode 100644 command/etc/command.urm.puml create mode 100644 composite/etc/composite.urm.puml create mode 100644 dao/etc/dao.urm.puml create mode 100644 data-mapper/etc/data-mapper.urm.puml create mode 100644 decorator/etc/decorator.urm.puml create mode 100644 delegation/etc/delegation.urm.puml create mode 100644 dependency-injection/etc/dependency-injection.urm.puml create mode 100644 double-checked-locking/etc/double-checked-locking.urm.puml create mode 100644 double-dispatch/etc/double-dispatch.urm.puml create mode 100644 event-aggregator/etc/event-aggregator.urm.puml create mode 100644 event-driven-architecture/etc/event-driven-architecture.urm.puml create mode 100644 execute-around/etc/execute-around.urm.puml create mode 100644 facade/etc/facade.urm.puml create mode 100644 factory-kit/etc/factory-kit.urm.puml create mode 100644 factory-method/etc/factory-method.urm.puml create mode 100644 feature-toggle/etc/feature-toggle.urm.puml create mode 100644 fluentinterface/etc/fluentinterface.urm.puml create mode 100644 flux/etc/flux.urm.puml create mode 100644 flyweight/etc/flyweight.urm.puml create mode 100644 front-controller/etc/front-controller.urm.puml create mode 100644 half-sync-half-async/etc/half-sync-half-async.urm.puml create mode 100644 hexagonal/etc/hexagonal.urm.puml create mode 100644 intercepting-filter/etc/intercepting-filter.urm.puml create mode 100644 interpreter/etc/interpreter.urm.puml create mode 100644 iterator/etc/iterator.urm.puml create mode 100644 layers/etc/layers.urm.puml create mode 100644 lazy-loading/etc/lazy-loading.urm.puml create mode 100644 mediator/etc/mediator.urm.puml create mode 100644 memento/etc/memento.urm.puml create mode 100644 message-channel/etc/message-channel.urm.puml create mode 100644 model-view-controller/etc/model-view-controller.urm.puml create mode 100644 model-view-presenter/etc/model-view-presenter.urm.puml create mode 100644 monad/etc/monad.urm.puml create mode 100644 monostate/etc/monostate.urm.puml create mode 100644 multiton/etc/multiton.urm.puml create mode 100644 mute-idiom/etc/mute-idiom.urm.puml create mode 100644 mutex/etc/mutex.urm.puml create mode 100644 naked-objects/etc/naked-objects-dom.urm.puml create mode 100644 naked-objects/etc/naked-objects-fixture.urm.puml create mode 100644 naked-objects/etc/naked-objects-integtests.urm.puml create mode 100644 null-object/etc/null-object.urm.puml create mode 100644 object-pool/etc/object-pool.urm.puml create mode 100644 observer/etc/observer.urm.puml create mode 100644 page-object/etc/page-object.urm.puml create mode 100644 poison-pill/etc/poison-pill.urm.puml create mode 100644 private-class-data/etc/private-class-data.urm.puml create mode 100644 producer-consumer/etc/producer-consumer.urm.puml create mode 100644 property/etc/property.urm.puml create mode 100644 prototype/etc/prototype.urm.puml create mode 100644 proxy/etc/proxy.urm.puml create mode 100644 publish-subscribe/etc/publish-subscribe.urm.puml create mode 100644 reactor/etc/reactor.urm.puml create mode 100644 reader-writer-lock/etc/reader-writer-lock.urm.puml create mode 100644 repository/etc/repository.urm.puml create mode 100644 resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml create mode 100644 semaphore/etc/semaphore.urm.puml create mode 100644 servant/etc/servant.urm.puml create mode 100644 service-layer/etc/service-layer.urm.puml create mode 100644 service-locator/etc/service-locator.urm.puml create mode 100644 singleton/etc/singleton.urm.puml create mode 100644 specification/etc/specification.urm.puml create mode 100644 state/etc/state.urm.puml create mode 100644 step-builder/etc/step-builder.urm.puml create mode 100644 strategy/etc/strategy.urm.puml create mode 100644 template-method/etc/template-method.urm.puml create mode 100644 thread-pool/etc/thread-pool.urm.puml create mode 100644 tolerant-reader/etc/tolerant-reader.urm.puml create mode 100644 twin/etc/twin.urm.puml create mode 100644 value-object/etc/value-object.urm.puml create mode 100644 visitor/etc/visitor.urm.puml diff --git a/abstract-document/etc/abstract-document.urm.puml b/abstract-document/etc/abstract-document.urm.puml new file mode 100644 index 000000000000..c738b50ce786 --- /dev/null +++ b/abstract-document/etc/abstract-document.urm.puml @@ -0,0 +1,59 @@ +@startuml +package com.iluwatar.abstractdocument.domain { + class Part { + + Part(properties : Map) + } + class Car { + + Car(properties : Map) + } + interface HasModel { + + PROPERTY : String {static} + + getModel() : Optional + } + interface HasParts { + + PROPERTY : String {static} + + getParts() : Stream + } + interface HasType { + + PROPERTY : String {static} + + getType() : Optional + } + interface HasPrice { + + PROPERTY : String {static} + + getPrice() : Optional + } +} +package com.iluwatar.abstractdocument { + interface Document { + + children(String, Function, T>) : Stream {abstract} + + get(String) : Object {abstract} + + put(String, Object) {abstract} + } + abstract class AbstractDocument { + - properties : Map + # AbstractDocument(properties : Map) + + children(key : String, constructor : Function, T>) : Stream + + get(key : String) : Object + + put(key : String, value : Object) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } +} +AbstractDocument --+ Map +Part ..|> HasType +Part ..|> HasModel +Part ..|> HasPrice +Part --|> AbstractDocument +Car ..|> HasModel +Car ..|> HasPrice +Car ..|> HasParts +Car --|> AbstractDocument +HasModel --|> Document +HasParts --|> Document +AbstractDocument ..|> Document +HasType --|> Document +HasPrice --|> Document +@enduml \ No newline at end of file diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml new file mode 100644 index 000000000000..88402c6d76a9 --- /dev/null +++ b/abstract-factory/etc/abstract-factory.urm.puml @@ -0,0 +1,88 @@ +@startuml +package com.iluwatar.abstractfactory { + interface Castle { + + getDescription() : String {abstract} + } + class OrcKingdomFactory { + + OrcKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } + class ElfKing { + ~ DESCRIPTION : String {static} + + ElfKing() + + getDescription() : String + } + interface King { + + getDescription() : String {abstract} + } + class App { + - 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 OrcKing { + ~ DESCRIPTION : String {static} + + OrcKing() + + getDescription() : String + } + class ElfKingdomFactory { + + ElfKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } + interface Army { + + getDescription() : String {abstract} + } + class OrcArmy { + ~ DESCRIPTION : String {static} + + OrcArmy() + + getDescription() : String + } + interface KingdomFactory { + + createArmy() : Army {abstract} + + createCastle() : Castle {abstract} + + createKing() : King {abstract} + } + class ElfArmy { + ~ DESCRIPTION : String {static} + + ElfArmy() + + getDescription() : String + } + class ElfCastle { + ~ DESCRIPTION : String {static} + + ElfCastle() + + getDescription() : String + } + class OrcCastle { + ~ DESCRIPTION : String {static} + + OrcCastle() + + getDescription() : String + } +} +App --> "-castle" Castle +App --> "-king" King +App --> "-army" Army +OrcKingdomFactory ..|> KingdomFactory +ElfKing ..|> King +OrcKing ..|> King +ElfKingdomFactory ..|> KingdomFactory +OrcArmy ..|> Army +ElfArmy ..|> Army +ElfCastle ..|> Castle +OrcCastle ..|> Castle +@enduml \ No newline at end of file diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml new file mode 100644 index 000000000000..2cee13dc434a --- /dev/null +++ b/adapter/etc/adapter.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.adapter { + class App { + + App() + + main(args : String[]) {static} + } + interface BattleShip { + + fire() {abstract} + + move() {abstract} + } + class Captain { + - battleship : BattleShip + + Captain() + + Captain(battleship : BattleShip) + + fire() + + move() + + setBattleship(battleship : BattleShip) + } + class BattleFishingBoat { + - boat : FishingBoat + + BattleFishingBoat() + + fire() + + move() + } + class FishingBoat { + + FishingBoat() + + fish() + + sail() + } +} +BattleFishingBoat --> "-boat" FishingBoat +Captain --> "-battleship" BattleShip +Captain ..|> BattleShip +BattleFishingBoat ..|> BattleShip +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-service.urm.puml b/aggregator-microservices/etc/aggregator-service.urm.puml new file mode 100644 index 000000000000..5c2e1167ab9c --- /dev/null +++ b/aggregator-microservices/etc/aggregator-service.urm.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.aggregator.microservices { + class Aggregator { + - informationClient : ProductInformationClient + - inventoryClient : ProductInventoryClient + + Aggregator() + + getProduct() : Product + } + class ProductInformationClientImpl { + + ProductInformationClientImpl() + + getProductTitle() : String + } + interface ProductInformationClient { + + getProductTitle() : String {abstract} + } + class Product { + - productInventories : int + - title : String + + Product() + + getProductInventories() : int + + getTitle() : String + + setProductInventories(productInventories : int) + + setTitle(title : String) + } + class ProductInventoryClientImpl { + + ProductInventoryClientImpl() + + getProductInventories() : int + } + class App { + + App() + + main(args : String[]) {static} + } + interface ProductInventoryClient { + + getProductInventories() : int {abstract} + } +} +Aggregator --> "-inventoryClient" ProductInventoryClient +Aggregator --> "-informationClient" ProductInformationClient +ProductInformationClientImpl ..|> ProductInformationClient +ProductInventoryClientImpl ..|> ProductInventoryClient +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/information-microservice.urm.puml b/aggregator-microservices/etc/information-microservice.urm.puml new file mode 100644 index 000000000000..e0a2ccb24fb2 --- /dev/null +++ b/aggregator-microservices/etc/information-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.information.microservice { + class InformationApplication { + + InformationApplication() + + main(args : String[]) {static} + } + class InformationController { + + InformationController() + + getProductTitle() : String + } +} +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/inventory-microservice.urm.puml b/aggregator-microservices/etc/inventory-microservice.urm.puml new file mode 100644 index 000000000000..90f327e07db8 --- /dev/null +++ b/aggregator-microservices/etc/inventory-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.inventory.microservice { + class InventoryApplication { + + InventoryApplication() + + main(args : String[]) {static} + } + class InventoryController { + + InventoryController() + + getProductInventories() : int + } +} +@enduml \ No newline at end of file diff --git a/api-gateway/etc/api-gateway-service.urm.puml b/api-gateway/etc/api-gateway-service.urm.puml new file mode 100644 index 000000000000..3313f7059cc5 --- /dev/null +++ b/api-gateway/etc/api-gateway-service.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.api.gateway { + interface ImageClient { + + getImagePath() : String {abstract} + } + class MobileProduct { + - price : String + + MobileProduct() + + getPrice() : String + + setPrice(price : String) + } + class ApiGateway { + - imageClient : ImageClient + - priceClient : PriceClient + + ApiGateway() + + getProductDesktop() : DesktopProduct + + getProductMobile() : MobileProduct + } + class DesktopProduct { + - imagePath : String + - price : String + + DesktopProduct() + + getImagePath() : String + + getPrice() : String + + setImagePath(imagePath : String) + + setPrice(price : String) + } + interface PriceClient { + + getPrice() : String {abstract} + } + class PriceClientImpl { + + PriceClientImpl() + + getPrice() : String + } + class ImageClientImpl { + + ImageClientImpl() + + getImagePath() : String + } + class App { + + App() + + main(args : String[]) {static} + } +} +ApiGateway --> "-imageClient" ImageClient +ApiGateway --> "-priceClient" PriceClient +PriceClientImpl ..|> PriceClient +ImageClientImpl ..|> ImageClient +@enduml \ No newline at end of file diff --git a/api-gateway/etc/image-microservice.urm.puml b/api-gateway/etc/image-microservice.urm.puml new file mode 100644 index 000000000000..130dac9de383 --- /dev/null +++ b/api-gateway/etc/image-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.image.microservice { + class ImageApplication { + + ImageApplication() + + main(args : String[]) {static} + } + class ImageController { + + ImageController() + + getImagePath() : String + } +} +@enduml \ No newline at end of file diff --git a/api-gateway/etc/price-microservice.urm.puml b/api-gateway/etc/price-microservice.urm.puml new file mode 100644 index 000000000000..9893c9c601d1 --- /dev/null +++ b/api-gateway/etc/price-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.price.microservice { + class PriceApplication { + + PriceApplication() + + main(args : String[]) {static} + } + class PriceController { + + PriceController() + + getPrice() : String + } +} +@enduml \ No newline at end of file diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml new file mode 100644 index 000000000000..9a90d307ecfd --- /dev/null +++ b/async-method-invocation/etc/async-method-invocation.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.async.method.invocation { + interface AsyncCallback { + + onComplete(T, Optional) {abstract} + } + interface AsyncResult { + + await() {abstract} + + getValue() : T {abstract} + + isCompleted() : boolean {abstract} + } + class ThreadAsyncExecutor { + - idx : AtomicInteger + + ThreadAsyncExecutor() + + endProcess(asyncResult : AsyncResult) : T + + startProcess(task : Callable) : AsyncResult + + startProcess(task : Callable, callback : AsyncCallback) : AsyncResult + } + class App { + + App() + - callback(name : String) : AsyncCallback {static} + - lazyval(value : T, delayMillis : long) : Callable {static} + - log(msg : String) {static} + + main(args : String[]) {static} + } + -class CompletableResult { + ~ COMPLETED : int {static} + ~ FAILED : int {static} + ~ RUNNING : int {static} + ~ callback : Optional> + ~ exception : Exception + ~ lock : Object + ~ state : int + ~ value : T + ~ CompletableResult(callback : AsyncCallback) + + await() + + getValue() : T + + isCompleted() : boolean + ~ setException(exception : Exception) + ~ setValue(value : T) + } + interface AsyncExecutor { + + endProcess(AsyncResult) : T {abstract} + + startProcess(Callable) : AsyncResult {abstract} + + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} + } +} +CompletableResult ..+ ThreadAsyncExecutor +ThreadAsyncExecutor ..|> AsyncExecutor +CompletableResult ..|> AsyncResult +@enduml \ No newline at end of file diff --git a/bridge/etc/bridge.urm.puml b/bridge/etc/bridge.urm.puml new file mode 100644 index 000000000000..d9d7a41456f0 --- /dev/null +++ b/bridge/etc/bridge.urm.puml @@ -0,0 +1,89 @@ +@startuml +package com.iluwatar.bridge { + class FlyingMagicWeapon { + + FlyingMagicWeapon(imp : FlyingMagicWeaponImpl) + + fly() + + getImp() : FlyingMagicWeaponImpl + + swing() + + unwield() + + wield() + } + abstract class MagicWeapon { + # imp : MagicWeaponImpl + + MagicWeapon(imp : MagicWeaponImpl) + + getImp() : MagicWeaponImpl + + swing() {abstract} + + unwield() {abstract} + + wield() {abstract} + } + abstract class SoulEatingMagicWeaponImpl { + + SoulEatingMagicWeaponImpl() + + eatSoulImp() {abstract} + } + class BlindingMagicWeapon { + + BlindingMagicWeapon(imp : BlindingMagicWeaponImpl) + + blind() + + getImp() : BlindingMagicWeaponImpl + + swing() + + unwield() + + wield() + } + class Stormbringer { + + Stormbringer() + + eatSoulImp() + + swingImp() + + unwieldImp() + + wieldImp() + } + abstract class BlindingMagicWeaponImpl { + + BlindingMagicWeaponImpl() + + blindImp() {abstract} + } + class SoulEatingMagicWeapon { + + SoulEatingMagicWeapon(imp : SoulEatingMagicWeaponImpl) + + eatSoul() + + getImp() : SoulEatingMagicWeaponImpl + + swing() + + unwield() + + wield() + } + abstract class MagicWeaponImpl { + + MagicWeaponImpl() + + swingImp() {abstract} + + unwieldImp() {abstract} + + wieldImp() {abstract} + } + class Excalibur { + + Excalibur() + + blindImp() + + swingImp() + + unwieldImp() + + wieldImp() + } + abstract class FlyingMagicWeaponImpl { + + FlyingMagicWeaponImpl() + + flyImp() {abstract} + } + class Mjollnir { + + Mjollnir() + + flyImp() + + swingImp() + + unwieldImp() + + wieldImp() + } + class App { + + App() + + main(args : String[]) {static} + } +} +MagicWeapon --> "-imp" MagicWeaponImpl +FlyingMagicWeapon --|> MagicWeapon +SoulEatingMagicWeaponImpl --|> MagicWeaponImpl +BlindingMagicWeapon --|> MagicWeapon +Stormbringer --|> SoulEatingMagicWeaponImpl +BlindingMagicWeaponImpl --|> MagicWeaponImpl +SoulEatingMagicWeapon --|> MagicWeapon +Excalibur --|> BlindingMagicWeaponImpl +FlyingMagicWeaponImpl --|> MagicWeaponImpl +Mjollnir --|> FlyingMagicWeaponImpl +@enduml \ No newline at end of file diff --git a/builder/etc/builder.urm.puml b/builder/etc/builder.urm.puml new file mode 100644 index 000000000000..2624763297ea --- /dev/null +++ b/builder/etc/builder.urm.puml @@ -0,0 +1,100 @@ +@startuml +package com.iluwatar.builder { + class Hero { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + - Hero(builder : Builder) + + getArmor() : Armor + + getHairColor() : HairColor + + getHairType() : HairType + + getName() : String + + getProfession() : Profession + + getWeapon() : Weapon + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class Builder { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + + Builder(profession : Profession, name : String) + + build() : Hero + + withArmor(armor : Armor) : Builder + + withHairColor(hairColor : HairColor) : Builder + + withHairType(hairType : HairType) : Builder + + withWeapon(weapon : Weapon) : Builder + } + enum Armor { + + CHAIN_MAIL {static} + + CLOTHES {static} + + LEATHER {static} + + PLATE_MAIL {static} + - title : String + + toString() : String + + valueOf(name : String) : Armor {static} + + values() : Armor[] {static} + } + enum Profession { + + MAGE {static} + + PRIEST {static} + + THIEF {static} + + WARRIOR {static} + + toString() : String + + valueOf(name : String) : Profession {static} + + values() : Profession[] {static} + } + enum Weapon { + + AXE {static} + + BOW {static} + + DAGGER {static} + + SWORD {static} + + WARHAMMER {static} + + toString() : String + + valueOf(name : String) : Weapon {static} + + values() : Weapon[] {static} + } + enum HairType { + + BALD {static} + + CURLY {static} + + LONG_CURLY {static} + + LONG_STRAIGHT {static} + + SHORT {static} + - title : String + + toString() : String + + valueOf(name : String) : HairType {static} + + values() : HairType[] {static} + } + enum HairColor { + + BLACK {static} + + BLOND {static} + + BROWN {static} + + RED {static} + + WHITE {static} + + toString() : String + + valueOf(name : String) : HairColor {static} + + values() : HairColor[] {static} + } +} +Builder ..+ Hero +Hero --> "-profession" Profession +Hero --> "-armor" Armor +App --+ Hero +Builder --> "-weapon" Weapon +Builder --> "-hairColor" HairColor +Builder --> "-hairType" HairType +Hero --> "-hairColor" HairColor +Builder --> "-profession" Profession +Hero --> "-weapon" Weapon +Hero --> "-hairType" HairType +Builder --> "-armor" Armor +@enduml \ No newline at end of file diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml new file mode 100644 index 000000000000..a8f31700b924 --- /dev/null +++ b/business-delegate/etc/business-delegate.urm.puml @@ -0,0 +1,55 @@ +@startuml +package com.iluwatar.business.delegate { + class BusinessLookup { + - ejbService : EjbService + - jmsService : JmsService + + BusinessLookup() + + getBusinessService(serviceType : ServiceType) : BusinessService + + setEjbService(ejbService : EjbService) + + setJmsService(jmsService : JmsService) + } + class Client { + - businessDelegate : BusinessDelegate + + Client(businessDelegate : BusinessDelegate) + + doTask() + } + class EjbService { + + EjbService() + + doProcessing() + } + class BusinessDelegate { + - businessService : BusinessService + - lookupService : BusinessLookup + - serviceType : ServiceType + + BusinessDelegate() + + doTask() + + setLookupService(businessLookup : BusinessLookup) + + setServiceType(serviceType : ServiceType) + } + interface BusinessService { + + doProcessing() {abstract} + } + class JmsService { + + JmsService() + + doProcessing() + } + class App { + + App() + + main(args : String[]) {static} + } + enum ServiceType { + + EJB {static} + + JMS {static} + + valueOf(name : String) : ServiceType {static} + + values() : ServiceType[] {static} + } +} +BusinessDelegate --> "-serviceType" ServiceType +BusinessLookup --> "-ejbService" EjbService +Client --> "-businessDelegate" BusinessDelegate +BusinessDelegate --> "-businessService" BusinessService +BusinessDelegate --> "-lookupService" BusinessLookup +BusinessLookup --> "-jmsService" JmsService +EjbService ..|> BusinessService +JmsService ..|> BusinessService +@enduml \ No newline at end of file diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml new file mode 100644 index 000000000000..273c9911c213 --- /dev/null +++ b/caching/etc/caching.urm.puml @@ -0,0 +1,100 @@ +@startuml +package com.iluwatar.caching { + class UserAccount { + - additionalInfo : String + - userId : String + - userName : String + + UserAccount(userId : String, userName : String, additionalInfo : String) + + getAdditionalInfo() : String + + getUserId() : String + + getUserName() : String + + setAdditionalInfo(additionalInfo : String) + + setUserId(userId : String) + + setUserName(userName : String) + + toString() : String + } + class CacheStore { + ~ cache : LruCache {static} + - CacheStore() + + clearCache() {static} + + flushCache() {static} + + initCapacity(capacity : int) {static} + + print() : String {static} + + readThrough(userId : String) : UserAccount {static} + + readThroughWithWriteBackPolicy(userId : String) : UserAccount {static} + + writeAround(userAccount : UserAccount) {static} + + writeBehind(userAccount : UserAccount) {static} + + writeThrough(userAccount : UserAccount) {static} + } + class AppManager { + - cachingPolicy : CachingPolicy {static} + - AppManager() + + find(userId : String) : UserAccount {static} + + initCacheCapacity(capacity : int) {static} + + initCachingPolicy(policy : CachingPolicy) {static} + + initDb(useMongoDb : boolean) {static} + + printCacheContent() : String {static} + + save(userAccount : UserAccount) {static} + } + ~class Node { + ~ next : Node + ~ previous : Node + ~ userAccount : UserAccount + ~ userId : String + + Node(this$0 : LruCache, userId : String, userAccount : UserAccount) + } + class LruCache { + ~ cache : Map + ~ capacity : int + ~ end : Node + ~ head : Node + + LruCache(capacity : int) + + clear() + + contains(userId : String) : boolean + + get(userId : String) : UserAccount + + getCacheDataInListForm() : List + + getLruData() : UserAccount + + invalidate(userId : String) + + isFull() : boolean + + remove(node : Node) + + set(userId : String, userAccount : UserAccount) + + setCapacity(newCapacity : int) + + setHead(node : Node) + } + class DbManager { + - db : MongoDatabase {static} + - mongoClient : MongoClient {static} + - useMongoDB : boolean {static} + - virtualDB : Map {static} + - DbManager() + + connect() {static} + + createVirtualDb() {static} + + readFromDb(userId : String) : UserAccount {static} + + updateDb(userAccount : UserAccount) {static} + + upsertDb(userAccount : UserAccount) {static} + + writeToDb(userAccount : UserAccount) {static} + } + class App { + + App() + + main(args : String[]) {static} + + useReadAndWriteThroughStrategy() + + useReadThroughAndWriteAroundStrategy() + + useReadThroughAndWriteBehindStrategy() + } + enum CachingPolicy { + + AROUND {static} + + BEHIND {static} + + THROUGH {static} + - policy : String + + getPolicy() : String + + valueOf(name : String) : CachingPolicy {static} + + values() : CachingPolicy[] {static} + } +} +Node --+ LruCache +LruCache --> "-head" Node +Node --> "-previous" Node +AppManager --> "-cachingPolicy" CachingPolicy +Node --> "-userAccount" UserAccount +CacheStore --> "-cache" LruCache +@enduml \ No newline at end of file diff --git a/callback/etc/callback.urm.puml b/callback/etc/callback.urm.puml new file mode 100644 index 000000000000..8b27ee8a8906 --- /dev/null +++ b/callback/etc/callback.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.callback { + class LambdasApp { + + LambdasApp() + + main(args : String[]) {static} + } + class SimpleTask { + + SimpleTask() + + execute() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Task { + + Task() + + execute() {abstract} + + executeWith(callback : Callback) + } + interface Callback { + + call() {abstract} + } +} +SimpleTask --|> Task +@enduml \ No newline at end of file diff --git a/chain/etc/chain.urm.puml b/chain/etc/chain.urm.puml new file mode 100644 index 000000000000..c75cbc8d1841 --- /dev/null +++ b/chain/etc/chain.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.chain { + class OrcCommander { + + OrcCommander(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class Request { + - handled : boolean + - requestDescription : String + - requestType : RequestType + + Request(requestType : RequestType, requestDescription : String) + + getRequestDescription() : String + + getRequestType() : RequestType + + isHandled() : boolean + + markHandled() + + toString() : String + } + class OrcOfficer { + + OrcOfficer(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class OrcKing { + ~ chain : RequestHandler + + OrcKing() + - buildChain() + + makeRequest(req : Request) + } + class OrcSoldier { + + OrcSoldier(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + abstract class RequestHandler { + - next : RequestHandler + + RequestHandler(next : RequestHandler) + + handleRequest(req : Request) + # printHandling(req : Request) + + toString() : String {abstract} + } + enum RequestType { + + COLLECT_TAX {static} + + DEFEND_CASTLE {static} + + TORTURE_PRISONER {static} + + valueOf(name : String) : RequestType {static} + + values() : RequestType[] {static} + } +} +RequestHandler --> "-next" RequestHandler +OrcKing --> "-chain" RequestHandler +Request --> "-requestType" RequestType +OrcCommander --|> RequestHandler +OrcOfficer --|> RequestHandler +OrcSoldier --|> RequestHandler +@enduml \ No newline at end of file diff --git a/command/etc/command.urm.puml b/command/etc/command.urm.puml new file mode 100644 index 000000000000..015fb30be54b --- /dev/null +++ b/command/etc/command.urm.puml @@ -0,0 +1,84 @@ +@startuml +package com.iluwatar.command { + abstract class Target { + - size : Size + - visibility : Visibility + + Target() + + getSize() : Size + + getVisibility() : Visibility + + printStatus() + + setSize(size : Size) + + setVisibility(visibility : Visibility) + + toString() : String {abstract} + } + class Goblin { + + Goblin() + + toString() : String + } + class ShrinkSpell { + - oldSize : Size + - target : Target + + ShrinkSpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + class InvisibilitySpell { + - target : Target + + InvisibilitySpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + class Wizard { + - redoStack : Deque + - undoStack : Deque + + Wizard() + + castSpell(command : Command, target : Target) + + redoLastSpell() + + toString() : String + + undoLastSpell() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Command { + + Command() + + execute(Target) {abstract} + + redo() {abstract} + + toString() : String {abstract} + + undo() {abstract} + } + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + + UNDEFINED {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } + enum Visibility { + + INVISIBLE {static} + + UNDEFINED {static} + + VISIBLE {static} + - title : String + + toString() : String + + valueOf(name : String) : Visibility {static} + + values() : Visibility[] {static} + } +} +Target --> "-size" Size +Wizard --> "-undoStack" Command +ShrinkSpell --> "-oldSize" Size +InvisibilitySpell --> "-target" Target +ShrinkSpell --> "-target" Target +Target --> "-visibility" Visibility +Goblin --|> Target +ShrinkSpell --|> Command +InvisibilitySpell --|> Command +@enduml \ No newline at end of file diff --git a/composite/etc/composite.urm.puml b/composite/etc/composite.urm.puml new file mode 100644 index 000000000000..6f6e93c98673 --- /dev/null +++ b/composite/etc/composite.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.composite { + class Letter { + - c : char + + Letter(c : char) + # printThisAfter() + # printThisBefore() + } + class Sentence { + + Sentence(words : List) + # printThisAfter() + # printThisBefore() + } + class Word { + + Word(letters : List) + # printThisAfter() + # printThisBefore() + } + class Messenger { + + Messenger() + ~ messageFromElves() : LetterComposite + ~ messageFromOrcs() : LetterComposite + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class LetterComposite { + - children : List + + LetterComposite() + + add(letter : LetterComposite) + + count() : int + + print() + # printThisAfter() {abstract} + # printThisBefore() {abstract} + } +} +LetterComposite --> "-children" LetterComposite +Letter --|> LetterComposite +Sentence --|> LetterComposite +Word --|> LetterComposite +@enduml \ No newline at end of file diff --git a/dao/etc/dao.urm.puml b/dao/etc/dao.urm.puml new file mode 100644 index 000000000000..f751b967c45b --- /dev/null +++ b/dao/etc/dao.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.dao { + class Customer { + - firstName : String + - id : int + - lastName : String + + Customer(id : int, firstName : String, lastName : String) + + equals(that : Object) : boolean + + getFirstName() : String + + getId() : int + + getLastName() : String + + hashCode() : int + + setFirstName(firstName : String) + + setId(id : int) + + setLastName(lastName : String) + + toString() : String + } + interface CustomerDao { + + add(Customer) : boolean {abstract} + + delete(Customer) : boolean {abstract} + + getAll() : Stream {abstract} + + getById(int) : Optional {abstract} + + update(Customer) : boolean {abstract} + } + class DbCustomerDao { + - dataSource : DataSource + + DbCustomerDao(dataSource : DataSource) + + add(customer : Customer) : boolean + - createCustomer(resultSet : ResultSet) : Customer + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + - getConnection() : Connection + - mutedClose(connection : Connection) + + update(customer : Customer) : boolean + } + class InMemoryCustomerDao { + - idToCustomer : Map + + InMemoryCustomerDao() + + add(customer : Customer) : boolean + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + + update(customer : Customer) : boolean + } + interface CustomerSchemaSql { + + CREATE_SCHEMA_SQL : String {static} + + DELETE_SCHEMA_SQL : String {static} + } + class App { + - DB_URL : String {static} + - log : Logger {static} + + App() + - addCustomers(customerDao : CustomerDao) {static} + - createDataSource() : DataSource {static} + - createSchema(dataSource : DataSource) {static} + - deleteSchema(dataSource : DataSource) {static} + + generateSampleCustomers() : List {static} + + main(args : String[]) {static} + - performOperationsUsing(customerDao : CustomerDao) {static} + } +} +DbCustomerDao ..|> CustomerDao +InMemoryCustomerDao ..|> CustomerDao +@enduml \ No newline at end of file diff --git a/data-mapper/etc/data-mapper.urm.puml b/data-mapper/etc/data-mapper.urm.puml new file mode 100644 index 000000000000..a16df0020089 --- /dev/null +++ b/data-mapper/etc/data-mapper.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.datamapper { + interface StudentDataMapper { + + delete(Student) {abstract} + + find(int) : Optional {abstract} + + insert(Student) {abstract} + + update(Student) {abstract} + } + class App { + - log : Logger {static} + - App() + + main(args : String[]) {static} + } + class Student { + - grade : char + - name : String + - serialVersionUID : long {static} + - studentId : int + + Student(studentId : int, name : String, grade : char) + + equals(inputObject : Object) : boolean + + getGrade() : char + + getName() : String + + getStudentId() : int + + hashCode() : int + + setGrade(grade : char) + + setName(name : String) + + setStudentId(studentId : int) + + toString() : String + } + class StudentDataMapperImpl { + - students : List + + StudentDataMapperImpl() + + delete(studentToBeDeleted : Student) + + find(studentId : int) : Optional + + getStudents() : List + + insert(studentToBeInserted : Student) + + update(studentToBeUpdated : Student) + } +} +StudentDataMapperImpl --> "-students" Student +StudentDataMapperImpl ..|> StudentDataMapper +@enduml \ No newline at end of file diff --git a/decorator/etc/decorator.urm.puml b/decorator/etc/decorator.urm.puml new file mode 100644 index 000000000000..6c44e6cc9d48 --- /dev/null +++ b/decorator/etc/decorator.urm.puml @@ -0,0 +1,29 @@ +@startuml +package com.iluwatar.decorator { + class App { + + App() + + main(args : String[]) {static} + } + class Troll { + + Troll() + + attack() + + fleeBattle() + + getAttackPower() : int + } + interface Hostile { + + attack() {abstract} + + fleeBattle() {abstract} + + getAttackPower() : int {abstract} + } + class SmartHostile { + - decorated : Hostile + + SmartHostile(decorated : Hostile) + + attack() + + fleeBattle() + + getAttackPower() : int + } +} +SmartHostile --> "-decorated" Hostile +Troll ..|> Hostile +SmartHostile ..|> Hostile +@enduml \ No newline at end of file diff --git a/delegation/etc/delegation.urm.puml b/delegation/etc/delegation.urm.puml new file mode 100644 index 000000000000..c143a6ba05cb --- /dev/null +++ b/delegation/etc/delegation.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.delegation.simple.printers { + class EpsonPrinter { + + EpsonPrinter() + + print(message : String) + } + class HpPrinter { + + HpPrinter() + + print(message : String) + } + class CanonPrinter { + + CanonPrinter() + + print(message : String) + } +} +package com.iluwatar.delegation.simple { + class PrinterController { + - printer : Printer + + PrinterController(printer : Printer) + + print(message : String) + } + interface Printer { + + print(String) {abstract} + } + class App { + + MESSAGE_TO_PRINT : String {static} + + App() + + main(args : String[]) {static} + } +} +PrinterController --> "-printer" Printer +PrinterController ..|> Printer +EpsonPrinter ..|> Printer +HpPrinter ..|> Printer +CanonPrinter ..|> Printer +@enduml \ No newline at end of file diff --git a/dependency-injection/etc/dependency-injection.urm.puml b/dependency-injection/etc/dependency-injection.urm.puml new file mode 100644 index 000000000000..c22c658adabe --- /dev/null +++ b/dependency-injection/etc/dependency-injection.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.dependency.injection { + interface Wizard { + + smoke() {abstract} + } + class GuiceWizard { + - tobacco : Tobacco + + GuiceWizard(tobacco : Tobacco) + + smoke() + } + class OldTobyTobacco { + + OldTobyTobacco() + } + abstract class Tobacco { + + Tobacco() + + smoke(wizard : Wizard) + } + class App { + + App() + + main(args : String[]) {static} + } + class RivendellTobacco { + + RivendellTobacco() + } + class AdvancedWizard { + - tobacco : Tobacco + + AdvancedWizard(tobacco : Tobacco) + + smoke() + } + class SecondBreakfastTobacco { + + SecondBreakfastTobacco() + } + class SimpleWizard { + - tobacco : OldTobyTobacco + + SimpleWizard() + + smoke() + } +} +SimpleWizard --> "-tobacco" OldTobyTobacco +AdvancedWizard --> "-tobacco" Tobacco +GuiceWizard --> "-tobacco" Tobacco +GuiceWizard ..|> Wizard +OldTobyTobacco --|> Tobacco +RivendellTobacco --|> Tobacco +AdvancedWizard ..|> Wizard +SecondBreakfastTobacco --|> Tobacco +SimpleWizard ..|> Wizard +@enduml \ No newline at end of file diff --git a/double-checked-locking/etc/double-checked-locking.urm.puml b/double-checked-locking/etc/double-checked-locking.urm.puml new file mode 100644 index 000000000000..6feb989019cc --- /dev/null +++ b/double-checked-locking/etc/double-checked-locking.urm.puml @@ -0,0 +1,20 @@ +@startuml +package com.iluwatar.doublechecked.locking { + class App { + + App() + + main(args : String[]) {static} + } + class Inventory { + - inventorySize : int + - items : List + - lock : Lock + + Inventory(inventorySize : int) + + addItem(item : Item) : boolean + + getItems() : List + } + class Item { + + Item() + } +} +Inventory --> "-items" Item +@enduml \ No newline at end of file diff --git a/double-dispatch/etc/double-dispatch.urm.puml b/double-dispatch/etc/double-dispatch.urm.puml new file mode 100644 index 000000000000..725f009c0baf --- /dev/null +++ b/double-dispatch/etc/double-dispatch.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.doubledispatch { + class App { + + App() + + main(args : String[]) {static} + } + class FlamingAsteroid { + + FlamingAsteroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + class SpaceStationIss { + + SpaceStationIss(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + abstract class GameObject { + - damaged : boolean + - onFire : boolean + + GameObject(left : int, top : int, right : int, bottom : int) + + collision(GameObject) {abstract} + + collisionResolve(FlamingAsteroid) {abstract} + + collisionResolve(Meteoroid) {abstract} + + collisionResolve(SpaceStationIss) {abstract} + + collisionResolve(SpaceStationMir) {abstract} + + isDamaged() : boolean + + isOnFire() : boolean + + setDamaged(damaged : boolean) + + setOnFire(onFire : boolean) + + toString() : String + } + class SpaceStationMir { + + SpaceStationMir(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + + collisionResolve(asteroid : FlamingAsteroid) + + collisionResolve(iss : SpaceStationIss) + + collisionResolve(meteoroid : Meteoroid) + + collisionResolve(mir : SpaceStationMir) + } + class Meteoroid { + + Meteoroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + + collisionResolve(asteroid : FlamingAsteroid) + + collisionResolve(iss : SpaceStationIss) + + collisionResolve(meteoroid : Meteoroid) + + collisionResolve(mir : SpaceStationMir) + } + class Rectangle { + - bottom : int + - left : int + - right : int + - top : int + + Rectangle(left : int, top : int, right : int, bottom : int) + + getBottom() : int + + getLeft() : int + + getRight() : int + + getTop() : int + ~ intersectsWith(r : Rectangle) : boolean + + toString() : String + } +} +FlamingAsteroid --|> Meteoroid +SpaceStationIss --|> SpaceStationMir +GameObject --|> Rectangle +SpaceStationMir --|> GameObject +Meteoroid --|> GameObject +@enduml \ No newline at end of file diff --git a/event-aggregator/etc/event-aggregator.urm.puml b/event-aggregator/etc/event-aggregator.urm.puml new file mode 100644 index 000000000000..18b7621c949e --- /dev/null +++ b/event-aggregator/etc/event-aggregator.urm.puml @@ -0,0 +1,73 @@ +@startuml +package com.iluwatar.event.aggregator { + class KingsHand { + + KingsHand() + + KingsHand(obs : EventObserver) + + onEvent(e : Event) + + timePasses(day : Weekday) + } + class KingJoffrey { + + KingJoffrey() + + onEvent(e : Event) + } + class Scout { + + Scout() + + Scout(obs : EventObserver) + + timePasses(day : Weekday) + } + class LordVarys { + + LordVarys() + + LordVarys(obs : EventObserver) + + timePasses(day : Weekday) + } + class App { + + App() + + main(args : String[]) {static} + } + class LordBaelish { + + LordBaelish() + + LordBaelish(obs : EventObserver) + + timePasses(day : Weekday) + } + abstract class EventEmitter { + - observers : List + + EventEmitter() + + EventEmitter(obs : EventObserver) + # notifyObservers(e : Event) + + registerObserver(obs : EventObserver) + + timePasses(Weekday) {abstract} + } + interface EventObserver { + + onEvent(Event) {abstract} + } + enum Weekday { + + FRIDAY {static} + + MONDAY {static} + + SATURDAY {static} + + SUNDAY {static} + + THURSDAY {static} + + TUESDAY {static} + + WEDNESDAY {static} + - description : String + + toString() : String + + valueOf(name : String) : Weekday {static} + + values() : Weekday[] {static} + } + enum Event { + + STARK_SIGHTED {static} + + TRAITOR_DETECTED {static} + + WARSHIPS_APPROACHING {static} + - description : String + + toString() : String + + valueOf(name : String) : Event {static} + + values() : Event[] {static} + } +} +EventEmitter --> "-observers" EventObserver +KingsHand ..|> EventObserver +KingsHand --|> EventEmitter +KingJoffrey ..|> EventObserver +Scout --|> EventEmitter +LordVarys --|> EventEmitter +LordBaelish --|> EventEmitter +@enduml \ No newline at end of file diff --git a/event-driven-architecture/etc/event-driven-architecture.urm.puml b/event-driven-architecture/etc/event-driven-architecture.urm.puml new file mode 100644 index 000000000000..55039190f101 --- /dev/null +++ b/event-driven-architecture/etc/event-driven-architecture.urm.puml @@ -0,0 +1,62 @@ +@startuml +package com.iluwatar.eda.handler { + class UserUpdatedEventHandler { + + UserUpdatedEventHandler() + + onEvent(event : UserUpdatedEvent) + } + class UserCreatedEventHandler { + + UserCreatedEventHandler() + + onEvent(event : UserCreatedEvent) + } +} +package com.iluwatar.eda.event { + abstract class AbstractEvent { + + AbstractEvent() + + getType() : Class + } + class UserUpdatedEvent { + - user : User + + UserUpdatedEvent(user : User) + + getUser() : User + } + class UserCreatedEvent { + - user : User + + UserCreatedEvent(user : User) + + getUser() : User + } +} +package com.iluwatar.eda.framework { + class EventDispatcher { + - handlers : Map, Handler> + + EventDispatcher() + + dispatch(event : E extends Event) + + registerHandler(eventType : Class, handler : Handler) + } + interface Event { + + getType() : Class {abstract} + } + interface Handler { + + onEvent(E extends Event) {abstract} + } +} +package com.iluwatar.eda.model { + class User { + - username : String + + User(username : String) + + getUsername() : String + } +} +package com.iluwatar.eda { + class App { + + App() + + main(args : String[]) {static} + } +} +UserCreatedEvent --> "-user" User +UserUpdatedEvent --> "-user" User +AbstractEvent ..|> Event +UserUpdatedEventHandler ..|> Handler +UserCreatedEventHandler ..|> Handler +UserUpdatedEvent --|> AbstractEvent +UserCreatedEvent --|> AbstractEvent +@enduml \ No newline at end of file diff --git a/execute-around/etc/execute-around.urm.puml b/execute-around/etc/execute-around.urm.puml new file mode 100644 index 000000000000..66d23ce7ad6e --- /dev/null +++ b/execute-around/etc/execute-around.urm.puml @@ -0,0 +1,14 @@ +@startuml +package com.iluwatar.execute.around { + interface FileWriterAction { + + writeFile(FileWriter) {abstract} + } + class SimpleFileWriter { + + SimpleFileWriter(filename : String, action : FileWriterAction) + } + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/facade/etc/facade.urm.puml b/facade/etc/facade.urm.puml new file mode 100644 index 000000000000..f72bc2b62468 --- /dev/null +++ b/facade/etc/facade.urm.puml @@ -0,0 +1,57 @@ +@startuml +package com.iluwatar.facade { + class DwarvenTunnelDigger { + + DwarvenTunnelDigger() + + name() : String + + work() + } + class DwarvenGoldmineFacade { + - workers : List + + DwarvenGoldmineFacade() + + digOutGold() + + endDay() + - makeActions(workers : Collection, actions : Action[]) {static} + + startNewDay() + } + class DwarvenGoldDigger { + + DwarvenGoldDigger() + + name() : String + + work() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class DwarvenMineWorker { + + DwarvenMineWorker() + - action(action : Action) + + action(actions : Action[]) + + goHome() + + goToMine() + + goToSleep() + + name() : String {abstract} + + wakeUp() + + work() {abstract} + } + class DwarvenCartOperator { + + DwarvenCartOperator() + + name() : String + + work() + } + ~enum Action { + + GO_HOME {static} + + GO_TO_MINE {static} + + GO_TO_SLEEP {static} + + WAKE_UP {static} + + WORK {static} + + valueOf(name : String) : Action {static} + + values() : Action[] {static} + } +} +DwarvenGoldmineFacade --+ DwarvenMineWorker +DwarvenGoldmineFacade --> "-workers" DwarvenMineWorker +Action ..+ DwarvenMineWorker +DwarvenTunnelDigger --|> DwarvenMineWorker +DwarvenGoldDigger --|> DwarvenMineWorker +DwarvenCartOperator --|> DwarvenMineWorker +@enduml \ No newline at end of file diff --git a/factory-kit/etc/factory-kit.urm.puml b/factory-kit/etc/factory-kit.urm.puml new file mode 100644 index 000000000000..faf5727eb918 --- /dev/null +++ b/factory-kit/etc/factory-kit.urm.puml @@ -0,0 +1,45 @@ +@startuml +package com.iluwatar.factorykit { + interface Builder { + + add(WeaponType, Supplier) {abstract} + } + class Spear { + + Spear() + + toString() : String + } + class Bow { + + Bow() + + toString() : String + } + class Sword { + + Sword() + + toString() : String + } + interface Weapon { + } + class App { + + App() + + main(args : String[]) {static} + } + class Axe { + + Axe() + + toString() : String + } + interface WeaponFactory { + + create(WeaponType) : Weapon {abstract} + + factory(consumer : Consumer) : WeaponFactory {static} + } + enum WeaponType { + + AXE {static} + + BOW {static} + + SPEAR {static} + + SWORD {static} + + valueOf(name : String) : WeaponType {static} + + values() : WeaponType[] {static} + } +} +Spear ..|> Weapon +Bow ..|> Weapon +Sword ..|> Weapon +Axe ..|> Weapon +@enduml \ No newline at end of file diff --git a/factory-method/etc/factory-method.urm.puml b/factory-method/etc/factory-method.urm.puml new file mode 100644 index 000000000000..ebc9d2ff7ae7 --- /dev/null +++ b/factory-method/etc/factory-method.urm.puml @@ -0,0 +1,53 @@ +@startuml +package com.iluwatar.factory.method { + class OrcBlacksmith { + + OrcBlacksmith() + + manufactureWeapon(weaponType : WeaponType) : Weapon + } + class ElfBlacksmith { + + ElfBlacksmith() + + manufactureWeapon(weaponType : WeaponType) : Weapon + } + class OrcWeapon { + - weaponType : WeaponType + + OrcWeapon(weaponType : WeaponType) + + getWeaponType() : WeaponType + + toString() : String + } + interface Blacksmith { + + manufactureWeapon(WeaponType) : Weapon {abstract} + } + interface Weapon { + + getWeaponType() : WeaponType {abstract} + } + class ElfWeapon { + - weaponType : WeaponType + + ElfWeapon(weaponType : WeaponType) + + getWeaponType() : WeaponType + + toString() : String + } + class App { + - blacksmith : Blacksmith + + App(blacksmith : Blacksmith) + + main(args : String[]) {static} + - manufactureWeapons() + } + enum WeaponType { + + AXE {static} + + SHORT_SWORD {static} + + SPEAR {static} + + UNDEFINED {static} + - title : String + + toString() : String + + valueOf(name : String) : WeaponType {static} + + values() : WeaponType[] {static} + } +} +ElfWeapon --> "-weaponType" WeaponType +OrcWeapon --> "-weaponType" WeaponType +App --> "-blacksmith" Blacksmith +OrcBlacksmith ..|> Blacksmith +ElfBlacksmith ..|> Blacksmith +OrcWeapon ..|> Weapon +ElfWeapon ..|> Weapon +@enduml \ No newline at end of file diff --git a/feature-toggle/etc/feature-toggle.urm.puml b/feature-toggle/etc/feature-toggle.urm.puml new file mode 100644 index 000000000000..762d49cb3ebe --- /dev/null +++ b/feature-toggle/etc/feature-toggle.urm.puml @@ -0,0 +1,47 @@ +@startuml +package com.iluwatar.featuretoggle.pattern { + interface Service { + + getWelcomeMessage(User) : String {abstract} + + isEnhanced() : boolean {abstract} + } +} +package com.iluwatar.featuretoggle.user { + class User { + - name : String + + User(name : String) + + toString() : String + } + class UserGroup { + - freeGroup : List {static} + - paidGroup : List {static} + + UserGroup() + + addUserToFreeGroup(user : User) {static} + + addUserToPaidGroup(user : User) {static} + + isPaid(user : User) : boolean {static} + } +} +package com.iluwatar.featuretoggle.pattern.propertiesversion { + class PropertiesFeatureToggleVersion { + - isEnhanced : boolean + + PropertiesFeatureToggleVersion(properties : Properties) + + getWelcomeMessage(user : User) : String + + isEnhanced() : boolean + } +} +package com.iluwatar.featuretoggle.pattern.tieredversion { + class TieredFeatureToggleVersion { + + TieredFeatureToggleVersion() + + getWelcomeMessage(user : User) : String + + isEnhanced() : boolean + } +} +package com.iluwatar.featuretoggle { + class App { + + App() + + main(args : String[]) {static} + } +} +UserGroup --> "-freeGroup" User +TieredFeatureToggleVersion ..|> Service +PropertiesFeatureToggleVersion ..|> Service +@enduml \ No newline at end of file diff --git a/fluentinterface/etc/fluentinterface.urm.puml b/fluentinterface/etc/fluentinterface.urm.puml new file mode 100644 index 000000000000..436fcb2e888a --- /dev/null +++ b/fluentinterface/etc/fluentinterface.urm.puml @@ -0,0 +1,71 @@ +@startuml +package com.iluwatar.fluentinterface.fluentiterable.simple { + class SimpleFluentIterable { + - iterable : Iterable + # SimpleFluentIterable(iterable : Iterable) + + asList() : List + + filter(predicate : Predicate) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + forEach(action : Consumer) + + from(iterable : Iterable) : FluentIterable {static} + + fromCopyOf(iterable : Iterable) : FluentIterable {static} + + getRemainingElementsCount() : int + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + + spliterator() : Spliterator + + toList(iterator : Iterator) : List {static} + } +} +package com.iluwatar.fluentinterface.app { + class App { + + App() + + main(args : String[]) {static} + - negatives() : Predicate {static} + - positives() : Predicate {static} + - prettyPrint(delimiter : String, prefix : String, iterable : Iterable) {static} + - prettyPrint(prefix : String, iterable : Iterable) {static} + - transformToString() : Function {static} + } +} +package com.iluwatar.fluentinterface.fluentiterable.lazy { + class LazyFluentIterable { + - iterable : Iterable + # LazyFluentIterable() + # LazyFluentIterable(iterable : Iterable) + + asList() : List + + filter(predicate : Predicate) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + from(iterable : Iterable) : FluentIterable {static} + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + } + abstract class DecoratingIterator { + # fromIterator : Iterator + - next : E + + DecoratingIterator(fromIterator : Iterator) + + computeNext() : E {abstract} + + hasNext() : boolean + + next() : E + } +} +package com.iluwatar.fluentinterface.fluentiterable { + interface FluentIterable { + + asList() : List {abstract} + + copyToList(iterable : Iterable) : List {static} + + filter(Predicate) : FluentIterable {abstract} + + first() : Optional {abstract} + + first(int) : FluentIterable {abstract} + + last() : Optional {abstract} + + last(int) : FluentIterable {abstract} + + map(Function) : FluentIterable {abstract} + } +} +LazyFluentIterable ..|> FluentIterable +SimpleFluentIterable ..|> FluentIterable +@enduml \ No newline at end of file diff --git a/flux/etc/flux.urm.puml b/flux/etc/flux.urm.puml new file mode 100644 index 000000000000..e4bece2fc589 --- /dev/null +++ b/flux/etc/flux.urm.puml @@ -0,0 +1,115 @@ +@startuml +package com.iluwatar.flux.view { + class ContentView { + - content : Content + + ContentView() + + render() + + storeChanged(store : Store) + } + class MenuView { + - selected : MenuItem + + MenuView() + + itemClicked(item : MenuItem) + + render() + + storeChanged(store : Store) + } + interface View { + + render() {abstract} + + storeChanged(Store) {abstract} + } +} +package com.iluwatar.flux.action { + class ContentAction { + - content : Content + + ContentAction(content : Content) + + getContent() : Content + } + class MenuAction { + - menuItem : MenuItem + + MenuAction(menuItem : MenuItem) + + getMenuItem() : MenuItem + } + abstract class Action { + - type : ActionType + + Action(type : ActionType) + + getType() : ActionType + } + enum MenuItem { + + COMPANY {static} + + HOME {static} + + PRODUCTS {static} + - title : String + + toString() : String + + valueOf(name : String) : MenuItem {static} + + values() : MenuItem[] {static} + } + enum Content { + + COMPANY {static} + + PRODUCTS {static} + - title : String + + toString() : String + + valueOf(name : String) : Content {static} + + values() : Content[] {static} + } + enum ActionType { + + CONTENT_CHANGED {static} + + MENU_ITEM_SELECTED {static} + + valueOf(name : String) : ActionType {static} + + values() : ActionType[] {static} + } +} +package com.iluwatar.flux.app { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.flux.store { + abstract class Store { + - views : List + + Store() + # notifyChange() + + onAction(Action) {abstract} + + registerView(view : View) + } + class ContentStore { + - content : Content + + ContentStore() + + getContent() : Content + + onAction(action : Action) + } + class MenuStore { + - selected : MenuItem + + MenuStore() + + getSelected() : MenuItem + + onAction(action : Action) + } +} +package com.iluwatar.flux.dispatcher { + class Dispatcher { + - instance : Dispatcher {static} + - stores : List + - Dispatcher() + - dispatchAction(action : Action) + + getInstance() : Dispatcher {static} + + menuItemSelected(menuItem : MenuItem) + + registerStore(store : Store) + } +} +MenuAction --> "-menuItem" MenuItem +Action --> "-type" ActionType +MenuStore --> "-selected" MenuItem +Dispatcher --> "-instance" Dispatcher +ContentView --> "-content" Content +Dispatcher --> "-stores" Store +MenuView --> "-selected" MenuItem +Store --> "-views" View +ContentStore --> "-content" Content +ContentAction --> "-content" Content +ContentAction --|> Action +ContentStore --|> Store +ContentView ..|> View +MenuAction --|> Action +MenuView ..|> View +MenuStore --|> Store +@enduml \ No newline at end of file diff --git a/flyweight/etc/flyweight.urm.puml b/flyweight/etc/flyweight.urm.puml new file mode 100644 index 000000000000..98a2b4721683 --- /dev/null +++ b/flyweight/etc/flyweight.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.flyweight { + class PoisonPotion { + + PoisonPotion() + + drink() + } + class StrengthPotion { + + StrengthPotion() + + drink() + } + class HealingPotion { + + HealingPotion() + + drink() + } + class PotionFactory { + - potions : Map + + PotionFactory() + ~ createPotion(type : PotionType) : Potion + } + interface Potion { + + drink() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class AlchemistShop { + - bottomShelf : List + - topShelf : List + + AlchemistShop() + + enumerate() + - fillShelves() + + getBottomShelf() : List + + getTopShelf() : List + } + class HolyWaterPotion { + + HolyWaterPotion() + + drink() + } + class InvisibilityPotion { + + InvisibilityPotion() + + drink() + } + enum PotionType { + + HEALING {static} + + HOLY_WATER {static} + + INVISIBILITY {static} + + POISON {static} + + STRENGTH {static} + + valueOf(name : String) : PotionType {static} + + values() : PotionType[] {static} + } +} +AlchemistShop --> "-topShelf" Potion +PoisonPotion ..|> Potion +StrengthPotion ..|> Potion +HealingPotion ..|> Potion +HolyWaterPotion ..|> Potion +InvisibilityPotion ..|> Potion +@enduml \ No newline at end of file diff --git a/front-controller/etc/front-controller.urm.puml b/front-controller/etc/front-controller.urm.puml new file mode 100644 index 000000000000..17ccebae133b --- /dev/null +++ b/front-controller/etc/front-controller.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.front.controller { + class App { + + App() + + main(args : String[]) {static} + } + class FrontController { + + FrontController() + - getCommand(request : String) : Command + - getCommandClass(request : String) : Class {static} + + handleRequest(request : String) + } + class ArcherView { + + ArcherView() + + display() + } + interface View { + + display() {abstract} + } + interface Command { + + process() {abstract} + } + class ErrorView { + + ErrorView() + + display() + } + class ArcherCommand { + + ArcherCommand() + + process() + } + class CatapultView { + + CatapultView() + + display() + } + class CatapultCommand { + + CatapultCommand() + + process() + } + class UnknownCommand { + + UnknownCommand() + + process() + } +} +ArcherView ..|> View +ErrorView ..|> View +ArcherCommand ..|> Command +CatapultView ..|> View +CatapultCommand ..|> Command +UnknownCommand ..|> Command +@enduml \ No newline at end of file diff --git a/half-sync-half-async/etc/half-sync-half-async.urm.puml b/half-sync-half-async/etc/half-sync-half-async.urm.puml new file mode 100644 index 000000000000..e733dd58659e --- /dev/null +++ b/half-sync-half-async/etc/half-sync-half-async.urm.puml @@ -0,0 +1,30 @@ +@startuml +package com.iluwatar.halfsynchalfasync { + class App { + + App() + - ap(i : long) : long {static} + + main(args : String[]) {static} + } + interface AsyncTask { + + call() : O {abstract} + + onError(Throwable) {abstract} + + onPostCall(O) {abstract} + + onPreCall() {abstract} + } + ~class ArithmeticSumTask { + - n : long + + ArithmeticSumTask(n : long) + + call() : Long + + onError(throwable : Throwable) + + onPostCall(result : Long) + + onPreCall() + } + class AsynchronousService { + - service : ExecutorService + + AsynchronousService(workQueue : BlockingQueue) + + execute(task : AsyncTask) + } +} +ArithmeticSumTask ..+ App +ArithmeticSumTask ..|> AsyncTask +@enduml \ No newline at end of file diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml new file mode 100644 index 000000000000..4102c5863b81 --- /dev/null +++ b/hexagonal/etc/hexagonal.urm.puml @@ -0,0 +1,183 @@ +@startuml +package com.iluwatar.hexagonal.service { + class LotteryServiceImpl { + - bank : WireTransfers + - notifications : LotteryNotifications + - repository : LotteryTicketRepository + + LotteryServiceImpl() + + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult + + submitTicket(ticket : LotteryTicket) : Optional + } + interface LotteryService { + + checkTicketForPrize(LotteryTicketId, LotteryNumbers) : LotteryTicketCheckResult {abstract} + + submitTicket(LotteryTicket) : Optional {abstract} + } +} +package com.iluwatar.hexagonal.domain { + class LotteryTicketId { + - id : UUID + + LotteryTicketId() + + getId() : UUID + } + class LotteryConstants { + + PLAYER_MAX_SALDO : int {static} + + PRIZE_AMOUNT : int {static} + + SERVICE_BANK_ACCOUNT : String {static} + + SERVICE_BANK_ACCOUNT_SALDO : int {static} + + TICKET_PRIZE : int {static} + + LotteryConstants() + } + class LotteryNumbers { + + MAX_NUMBER : int {static} + + MIN_NUMBER : int {static} + + NUM_NUMBERS : int {static} + - numbers : Set + - LotteryNumbers() + - LotteryNumbers(givenNumbers : Set) + + create(givenNumbers : Set) : LotteryNumbers {static} + + createRandom() : LotteryNumbers {static} + + equals(obj : Object) : boolean + - generateRandomNumbers() + + getNumbers() : Set + + hashCode() : int + } + class PlayerDetails { + - bankAccountNumber : String + - emailAddress : String + - phoneNumber : String + - PlayerDetails(email : String, bankAccount : String, phone : String) + + create(email : String, bankAccount : String, phone : String) : PlayerDetails {static} + + equals(obj : Object) : boolean + + getBankAccount() : String + + getEmail() : String + + getPhoneNumber() : String + + hashCode() : int + } + class LotteryTicketCheckResult { + - checkResult : CheckResult + - prizeAmount : int + + LotteryTicketCheckResult(result : CheckResult) + + LotteryTicketCheckResult(result : CheckResult, amount : int) + + equals(obj : Object) : boolean + + getPrizeAmount() : int + + getResult() : CheckResult + + hashCode() : int + } + class LotteryTicket { + - lotteryNumbers : LotteryNumbers + - playerDetails : PlayerDetails + - LotteryTicket(details : PlayerDetails, numbers : LotteryNumbers) + + create(details : PlayerDetails, numbers : LotteryNumbers) : LotteryTicket {static} + + equals(obj : Object) : boolean + + getNumbers() : LotteryNumbers + + getPlayerDetails() : PlayerDetails + + hashCode() : int + } + -class RandomNumberGenerator { + - randomIterator : OfInt + + RandomNumberGenerator(min : int, max : int) + + nextInt() : int + } + enum CheckResult { + + NO_PRIZE {static} + + TICKET_NOT_SUBMITTED {static} + + WIN_PRIZE {static} + + valueOf(name : String) : CheckResult {static} + + values() : CheckResult[] {static} + } +} +package com.iluwatar.hexagonal.banking { + class WireTransfersImpl { + - accounts : Map {static} + + WireTransfersImpl() + + getFunds(bankAccount : String) : int + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean + } + interface WireTransfers { + + getFunds(String) : int {abstract} + + setFunds(String, int) {abstract} + + transferFunds(int, String, String) : boolean {abstract} + } +} +package com.iluwatar.hexagonal.database { + class LotteryTicketInMemoryRepository { + - tickets : Map {static} + + LotteryTicketInMemoryRepository() + + deleteAll() + + findAll() : Map + + findById(id : LotteryTicketId) : Optional + + save(ticket : LotteryTicket) : Optional + } + interface LotteryTicketRepository { + + deleteAll() {abstract} + + findAll() : Map {abstract} + + findById(LotteryTicketId) : Optional {abstract} + + save(LotteryTicket) : Optional {abstract} + } +} +package com.iluwatar.hexagonal.notifications { + interface LotteryNotifications { + + notifyNoWin(PlayerDetails) {abstract} + + notifyPrize(PlayerDetails, int) {abstract} + + notifyPrizeError(PlayerDetails, int) {abstract} + + notifyTicketSubmitError(PlayerDetails) {abstract} + + notifyTicketSubmitted(PlayerDetails) {abstract} + } + class LotteryNotificationsImpl { + + LotteryNotificationsImpl() + + notifyNoWin(details : PlayerDetails) + + notifyPrize(details : PlayerDetails, prizeAmount : int) + + notifyPrizeError(details : PlayerDetails, prizeAmount : int) + + notifyTicketSubmitError(details : PlayerDetails) + + notifyTicketSubmitted(details : PlayerDetails) + } +} +package com.iluwatar.hexagonal { + class App { + - allPlayerDetails : List {static} + + App() + - getRandomPlayerDetails() : PlayerDetails {static} + + main(args : String[]) {static} + - submitTickets(lotteryService : LotteryService, numTickets : int) {static} + } +} +package com.iluwatar.hexagonal.administration { + interface LotteryAdministration { + + getAllSubmittedTickets() : Map {abstract} + + performLottery() : LotteryNumbers {abstract} + + resetLottery() {abstract} + } + class LotteryAdministrationImpl { + - bank : WireTransfers + - notifications : LotteryNotifications + - repository : LotteryTicketRepository + - service : LotteryService + + LotteryAdministrationImpl() + + getAllSubmittedTickets() : Map + + performLottery() : LotteryNumbers + + resetLottery() + } +} +LotteryTicket --> "-playerDetails" PlayerDetails +LotteryAdministrationImpl --> "-bank" WireTransfers +App --> "-allPlayerDetails" PlayerDetails +RandomNumberGenerator ..+ PrimitiveIterator +LotteryAdministrationImpl --> "-repository" LotteryTicketRepository +LotteryAdministrationImpl --+ LotteryTicketCheckResult +LotteryServiceImpl --> "-notifications" LotteryNotifications +LotteryTicket --> "-lotteryNumbers" LotteryNumbers +LotteryAdministrationImpl --> "-notifications" LotteryNotifications +LotteryServiceImpl --> "-repository" LotteryTicketRepository +LotteryServiceImpl --+ LotteryTicketCheckResult +LotteryServiceImpl --> "-bank" WireTransfers +RandomNumberGenerator ..+ LotteryNumbers +LotteryAdministrationImpl --> "-service" LotteryService +LotteryTicketCheckResult --> "-checkResult" CheckResult +CheckResult ..+ LotteryTicketCheckResult +LotteryTicketInMemoryRepository ..|> LotteryTicketRepository +WireTransfersImpl ..|> WireTransfers +LotteryServiceImpl ..|> LotteryService +LotteryNotificationsImpl ..|> LotteryNotifications +LotteryAdministrationImpl ..|> LotteryAdministration +@enduml \ No newline at end of file diff --git a/intercepting-filter/etc/intercepting-filter.urm.puml b/intercepting-filter/etc/intercepting-filter.urm.puml new file mode 100644 index 000000000000..f5bfb54e4214 --- /dev/null +++ b/intercepting-filter/etc/intercepting-filter.urm.puml @@ -0,0 +1,88 @@ +@startuml +package com.iluwatar.intercepting.filter { + interface Filter { + + execute(Order) : String {abstract} + + getLast() : Filter {abstract} + + getNext() : Filter {abstract} + + setNext(Filter) {abstract} + } + abstract class AbstractFilter { + - next : Filter + + AbstractFilter() + + AbstractFilter(next : Filter) + + execute(order : Order) : String + + getLast() : Filter + + getNext() : Filter + + setNext(filter : Filter) + } + class ContactFilter { + + ContactFilter() + + execute(order : Order) : String + } + class OrderFilter { + + OrderFilter() + + execute(order : Order) : String + } + class Order { + - address : String + - contactNumber : String + - depositNumber : String + - name : String + - order : String + + Order() + + Order(name : String, contactNumber : String, address : String, depositNumber : String, order : String) + + getAddress() : String + + getContactNumber() : String + + getDepositNumber() : String + + getName() : String + + getOrder() : String + + setAddress(address : String) + + setContactNumber(contactNumber : String) + + setDepositNumber(depositNumber : String) + + setName(name : String) + + setOrder(order : String) + } + class AddressFilter { + + AddressFilter() + + execute(order : Order) : String + } + ~class DListener { + ~ DListener(this$0 : Target) + + actionPerformed(e : ActionEvent) + } + class FilterManager { + - filterChain : FilterChain + + FilterManager() + + addFilter(filter : Filter) + + filterRequest(order : Order) : String + } + class FilterChain { + - chain : Filter + + FilterChain() + + addFilter(filter : Filter) + + execute(order : Order) : String + } + class DepositFilter { + + DepositFilter() + + execute(order : Order) : String + } + class App { + + App() + + main(args : String[]) {static} + } + class NameFilter { + + NameFilter() + + execute(order : Order) : String + } +} +AbstractFilter --> "-next" Filter +DListener --+ Target +FilterChain --> "-chain" Filter +FilterManager --> "-filterChain" FilterChain +AbstractFilter ..|> Filter +ContactFilter --|> AbstractFilter +OrderFilter --|> AbstractFilter +AddressFilter --|> AbstractFilter +DepositFilter --|> AbstractFilter +NameFilter --|> AbstractFilter +@enduml \ No newline at end of file diff --git a/interpreter/etc/interpreter.urm.puml b/interpreter/etc/interpreter.urm.puml new file mode 100644 index 000000000000..bdbd369d63b2 --- /dev/null +++ b/interpreter/etc/interpreter.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.interpreter { + abstract class Expression { + + Expression() + + interpret() : int {abstract} + + toString() : String {abstract} + } + class PlusExpression { + - leftExpression : Expression + - rightExpression : Expression + + PlusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class App { + + App() + + getOperatorInstance(s : String, left : Expression, right : Expression) : Expression {static} + + isOperator(s : String) : boolean {static} + + main(args : String[]) {static} + } + class NumberExpression { + - number : int + + NumberExpression(number : int) + + NumberExpression(s : String) + + interpret() : int + + toString() : String + } + class MultiplyExpression { + - leftExpression : Expression + - rightExpression : Expression + + MultiplyExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class MinusExpression { + - leftExpression : Expression + - rightExpression : Expression + + MinusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } +} +MultiplyExpression --> "-leftExpression" Expression +MinusExpression --> "-leftExpression" Expression +PlusExpression --> "-leftExpression" Expression +PlusExpression --|> Expression +NumberExpression --|> Expression +MultiplyExpression --|> Expression +MinusExpression --|> Expression +@enduml \ No newline at end of file diff --git a/iterator/etc/iterator.urm.puml b/iterator/etc/iterator.urm.puml new file mode 100644 index 000000000000..cbafd65959f5 --- /dev/null +++ b/iterator/etc/iterator.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.iterator { + interface ItemIterator { + + hasNext() : boolean {abstract} + + next() : Item {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class TreasureChestItemIterator { + - chest : TreasureChest + - idx : int + - type : ItemType + + TreasureChestItemIterator(chest : TreasureChest, type : ItemType) + - findNextIdx() : int + + hasNext() : boolean + + next() : Item + } + class TreasureChest { + - items : List + + TreasureChest() + + getItems() : List + ~ iterator(itemType : ItemType) : ItemIterator + } + class Item { + - name : String + - type : ItemType + + Item(type : ItemType, name : String) + + getType() : ItemType + + setType(type : ItemType) + + toString() : String + } + enum ItemType { + + ANY {static} + + POTION {static} + + RING {static} + + WEAPON {static} + + valueOf(name : String) : ItemType {static} + + values() : ItemType[] {static} + } +} +Item --> "-type" ItemType +TreasureChest --> "-items" Item +TreasureChestItemIterator --> "-type" ItemType +TreasureChestItemIterator --> "-chest" TreasureChest +TreasureChestItemIterator ..|> ItemIterator +@enduml \ No newline at end of file diff --git a/layers/etc/layers.urm.puml b/layers/etc/layers.urm.puml new file mode 100644 index 000000000000..d67216ff8885 --- /dev/null +++ b/layers/etc/layers.urm.puml @@ -0,0 +1,125 @@ +@startuml +package com.iluwatar.layers { + interface View { + + render() {abstract} + } + class CakeBakingServiceImpl { + - context : AbstractApplicationContext + + CakeBakingServiceImpl() + + bakeNewCake(cakeInfo : CakeInfo) + + getAllCakes() : List + - getAvailableLayerEntities() : List + + getAvailableLayers() : List + - getAvailableToppingEntities() : List + + getAvailableToppings() : List + + saveNewLayer(layerInfo : CakeLayerInfo) + + saveNewTopping(toppingInfo : CakeToppingInfo) + } + interface CakeDao { + } + class CakeTopping { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeTopping() + + CakeTopping(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } + class CakeLayerInfo { + + calories : int + + id : Optional + + name : String + + CakeLayerInfo(id : Long, name : String, calories : int) + + CakeLayerInfo(name : String, calories : int) + + toString() : String + } + interface CakeLayerDao { + } + interface CakeToppingDao { + } + interface CakeBakingService { + + bakeNewCake(CakeInfo) {abstract} + + getAllCakes() : List {abstract} + + getAvailableLayers() : List {abstract} + + getAvailableToppings() : List {abstract} + + saveNewLayer(CakeLayerInfo) {abstract} + + saveNewTopping(CakeToppingInfo) {abstract} + } + class CakeViewImpl { + - cakeBakingService : CakeBakingService + + CakeViewImpl(cakeBakingService : CakeBakingService) + + render() + } + class CakeToppingInfo { + + calories : int + + id : Optional + + name : String + + CakeToppingInfo(id : Long, name : String, calories : int) + + CakeToppingInfo(name : String, calories : int) + + toString() : String + } + class CakeInfo { + + cakeLayerInfos : List + + cakeToppingInfo : CakeToppingInfo + + id : Optional + + CakeInfo(cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + CakeInfo(id : Long, cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + calculateTotalCalories() : int + + toString() : String + } + class App { + - cakeBakingService : CakeBakingService {static} + + App() + - initializeData(cakeBakingService : CakeBakingService) {static} + + main(args : String[]) {static} + } + class Cake { + - id : Long + - layers : Set + - topping : CakeTopping + + Cake() + + addLayer(layer : CakeLayer) + + getId() : Long + + getLayers() : Set + + getTopping() : CakeTopping + + setId(id : Long) + + setLayers(layers : Set) + + setTopping(topping : CakeTopping) + + toString() : String + } + class CakeLayer { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeLayer() + + CakeLayer(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } +} +CakeViewImpl --> "-cakeBakingService" CakeBakingService +CakeInfo --> "-cakeToppingInfo" CakeToppingInfo +CakeInfo --> "-cakeLayerInfos" CakeLayerInfo +App --> "-cakeBakingService" CakeBakingService +CakeLayer --> "-cake" Cake +Cake --> "-topping" CakeTopping +CakeBakingServiceImpl ..|> CakeBakingService +CakeViewImpl ..|> View +@enduml \ No newline at end of file diff --git a/lazy-loading/etc/lazy-loading.urm.puml b/lazy-loading/etc/lazy-loading.urm.puml new file mode 100644 index 000000000000..96c42755350e --- /dev/null +++ b/lazy-loading/etc/lazy-loading.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.lazy.loading { + ~class HeavyFactory { + - heavyInstance : Heavy + ~ HeavyFactory(this$0 : Java8Holder) + + get() : Heavy + } + class HolderNaive { + - heavy : Heavy + + HolderNaive() + + getHeavy() : Heavy + } + class Heavy { + + Heavy() + } + class HolderThreadSafe { + - heavy : Heavy + + HolderThreadSafe() + + getHeavy() : Heavy + } + class Java8Holder { + - heavy : Supplier + + Java8Holder() + - createAndCacheHeavy() : Heavy + + getHeavy() : Heavy + } + class App { + + App() + + main(args : String[]) {static} + } +} +HolderThreadSafe --> "-heavy" Heavy +HolderNaive --> "-heavy" Heavy +HeavyFactory --> "-heavyInstance" Heavy +@enduml \ No newline at end of file diff --git a/mediator/etc/mediator.urm.puml b/mediator/etc/mediator.urm.puml new file mode 100644 index 000000000000..0b3baab5a894 --- /dev/null +++ b/mediator/etc/mediator.urm.puml @@ -0,0 +1,68 @@ +@startuml +package com.iluwatar.mediator { + class App { + + App() + + main(args : String[]) {static} + } + class Hobbit { + + Hobbit() + + toString() : String + } + interface PartyMember { + + act(Action) {abstract} + + joinedParty(Party) {abstract} + + partyAction(Action) {abstract} + } + interface Party { + + act(PartyMember, Action) {abstract} + + addMember(PartyMember) {abstract} + } + class Wizard { + + Wizard() + + toString() : String + } + class PartyImpl { + - members : List + + PartyImpl() + + act(actor : PartyMember, action : Action) + + addMember(member : PartyMember) + } + class Hunter { + + Hunter() + + toString() : String + } + class Rogue { + + Rogue() + + toString() : String + } + abstract class PartyMemberBase { + # party : Party + + PartyMemberBase() + + act(action : Action) + + joinedParty(party : Party) + + partyAction(action : Action) + + toString() : String {abstract} + } + enum Action { + + ENEMY {static} + + GOLD {static} + + HUNT {static} + + NONE {static} + + TALE {static} + - description : String + - title : String + + getDescription() : String + + toString() : String + + valueOf(name : String) : Action {static} + + values() : Action[] {static} + } +} +PartyImpl --> "-members" PartyMember +PartyMemberBase --> "-party" Party +Hobbit --|> PartyMemberBase +Wizard --|> PartyMemberBase +PartyImpl ..|> Party +Hunter --|> PartyMemberBase +Rogue --|> PartyMemberBase +PartyMemberBase ..|> PartyMember +@enduml \ No newline at end of file diff --git a/memento/etc/memento.urm.puml b/memento/etc/memento.urm.puml new file mode 100644 index 000000000000..aa63b4ebef65 --- /dev/null +++ b/memento/etc/memento.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.memento { + class Star { + - ageYears : int + - massTons : int + - type : StarType + + Star(startType : StarType, startAge : int, startMass : int) + ~ getMemento() : StarMemento + ~ setMemento(memento : StarMemento) + + timePasses() + + toString() : String + } + interface StarMemento { + } + -class StarMementoInternal { + - ageYears : int + - massTons : int + - type : StarType + - StarMementoInternal() + + getAgeYears() : int + + getMassTons() : int + + getType() : StarType + + setAgeYears(ageYears : int) + + setMassTons(massTons : int) + + setType(type : StarType) + } + class App { + + App() + + main(args : String[]) {static} + } + enum StarType { + + DEAD {static} + + RED_GIANT {static} + + SUN {static} + + SUPERNOVA {static} + + UNDEFINED {static} + + WHITE_DWARF {static} + - title : String + + toString() : String + + valueOf(name : String) : StarType {static} + + values() : StarType[] {static} + } +} +StarMementoInternal --> "-type" StarType +Star --> "-type" StarType +StarMementoInternal ..+ Star +StarMementoInternal ..|> StarMemento +@enduml \ No newline at end of file diff --git a/message-channel/etc/message-channel.urm.puml b/message-channel/etc/message-channel.urm.puml new file mode 100644 index 000000000000..9e2b24032f1e --- /dev/null +++ b/message-channel/etc/message-channel.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.message.channel { + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/model-view-controller/etc/model-view-controller.urm.puml b/model-view-controller/etc/model-view-controller.urm.puml new file mode 100644 index 000000000000..f8137bdae033 --- /dev/null +++ b/model-view-controller/etc/model-view-controller.urm.puml @@ -0,0 +1,69 @@ +@startuml +package com.iluwatar.model.view.controller { + class GiantModel { + - fatigue : Fatigue + - health : Health + - nourishment : Nourishment + ~ GiantModel(health : Health, fatigue : Fatigue, nourishment : Nourishment) + + getFatigue() : Fatigue + + getHealth() : Health + + getNourishment() : Nourishment + + setFatigue(fatigue : Fatigue) + + setHealth(health : Health) + + setNourishment(nourishment : Nourishment) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class GiantView { + + GiantView() + + displayGiant(giant : GiantModel) + } + class GiantController { + - giant : GiantModel + - view : GiantView + + GiantController(giant : GiantModel, view : GiantView) + + getFatigue() : Fatigue + + getHealth() : Health + + getNourishment() : Nourishment + + setFatigue(fatigue : Fatigue) + + setHealth(health : Health) + + setNourishment(nourishment : Nourishment) + + updateView() + } + enum Nourishment { + + HUNGRY {static} + + SATURATED {static} + + STARVING {static} + - title : String + + toString() : String + + valueOf(name : String) : Nourishment {static} + + values() : Nourishment[] {static} + } + enum Fatigue { + + ALERT {static} + + SLEEPING {static} + + TIRED {static} + - title : String + + toString() : String + + valueOf(name : String) : Fatigue {static} + + values() : Fatigue[] {static} + } + enum Health { + + DEAD {static} + + HEALTHY {static} + + WOUNDED {static} + - title : String + + toString() : String + + valueOf(name : String) : Health {static} + + values() : Health[] {static} + } +} +GiantModel --> "-nourishment" Nourishment +GiantController --> "-giant" GiantModel +GiantModel --> "-fatigue" Fatigue +GiantModel --> "-health" Health +GiantController --> "-view" GiantView +@enduml \ No newline at end of file diff --git a/model-view-presenter/etc/model-view-presenter.urm.puml b/model-view-presenter/etc/model-view-presenter.urm.puml new file mode 100644 index 000000000000..64bcfba32dd1 --- /dev/null +++ b/model-view-presenter/etc/model-view-presenter.urm.puml @@ -0,0 +1,87 @@ +@startuml +package com.iluwatar.model.view.presenter { + class FileLoader { + - fileName : String + - loaded : boolean + + FileLoader() + + fileExists() : boolean + + getFileName() : String + + isLoaded() : boolean + + loadData() : String + + setFileName(fileName : String) + } + class FileSelectorJFrame { + - area : JTextArea + - cancel : JButton + - contents : JLabel + - fileName : String + - info : JLabel + - input : JTextField + - ok : JButton + - panel : JPanel + - presenter : FileSelectorPresenter + - serialVersionUID : long {static} + + FileSelectorJFrame() + + actionPerformed(e : ActionEvent) + + close() + + displayData(data : String) + + getFileName() : String + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } + class App { + + App() + + main(args : String[]) {static} + } + interface FileSelectorView { + + close() {abstract} + + displayData(String) {abstract} + + getFileName() : String {abstract} + + getPresenter() : FileSelectorPresenter {abstract} + + isOpened() : boolean {abstract} + + open() {abstract} + + setFileName(String) {abstract} + + setPresenter(FileSelectorPresenter) {abstract} + + showMessage(String) {abstract} + } + class FileSelectorStub { + - dataDisplayed : boolean + - name : String + - numOfMessageSent : int + - opened : boolean + - presenter : FileSelectorPresenter + + FileSelectorStub() + + close() + + dataDisplayed() : boolean + + displayData(data : String) + + getFileName() : String + + getMessagesSent() : int + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } + class FileSelectorPresenter { + - loader : FileLoader + - view : FileSelectorView + + FileSelectorPresenter(view : FileSelectorView) + + cancelled() + + confirmed() + + fileNameChanged() + + setLoader(loader : FileLoader) + + start() + } +} +FileSelectorStub --> "-presenter" FileSelectorPresenter +FileSelectorJFrame --> "-presenter" FileSelectorPresenter +FileSelectorPresenter --> "-loader" FileLoader +FileSelectorPresenter --> "-view" FileSelectorView +FileSelectorJFrame ..|> FileSelectorView +FileSelectorStub ..|> FileSelectorView +@enduml \ No newline at end of file diff --git a/monad/etc/monad.urm.puml b/monad/etc/monad.urm.puml new file mode 100644 index 000000000000..6fe037064dcd --- /dev/null +++ b/monad/etc/monad.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.monad { + class Validator { + - exceptions : List + - t : T + - Validator(t : T) + + get() : T + + of(t : T) : Validator {static} + + validate(projection : Function, validation : Predicate, message : String) : Validator + + validate(validation : Predicate, message : String) : Validator + } + class App { + + App() + + main(args : String[]) {static} + } + class User { + - age : int + - email : String + - name : String + - sex : Sex + + User(name : String, age : int, sex : Sex, email : String) + + getAge() : int + + getEmail() : String + + getName() : String + + getSex() : Sex + } + enum Sex { + + FEMALE {static} + + MALE {static} + + valueOf(name : String) : Sex {static} + + values() : Sex[] {static} + } +} +User --> "-sex" Sex +@enduml \ No newline at end of file diff --git a/monostate/etc/monostate.urm.puml b/monostate/etc/monostate.urm.puml new file mode 100644 index 000000000000..3c09bb4ed57e --- /dev/null +++ b/monostate/etc/monostate.urm.puml @@ -0,0 +1,32 @@ +@startuml +package com.iluwatar.monostate { + class LoadBalancer { + - id : int {static} + - lastServedId : int {static} + - servers : List {static} + + LoadBalancer() + + addServer(server : Server) + + getLastServedId() : int {static} + + getNoOfServers() : int + + serverRequest(request : Request) + } + class App { + + App() + + main(args : String[]) {static} + } + class Request { + + value : String + + Request(value : String) + } + class Server { + + host : String + + id : int + + port : int + + Server(host : String, port : int, id : int) + + getHost() : String + + getPort() : int + + serve(request : Request) + } +} +LoadBalancer --> "-servers" Server +@enduml \ No newline at end of file diff --git a/multiton/etc/multiton.urm.puml b/multiton/etc/multiton.urm.puml new file mode 100644 index 000000000000..c582a6379ce0 --- /dev/null +++ b/multiton/etc/multiton.urm.puml @@ -0,0 +1,29 @@ +@startuml +package com.iluwatar.multiton { + class App { + + App() + + main(args : String[]) {static} + } + class Nazgul { + - name : NazgulName + - nazguls : Map {static} + - Nazgul(name : NazgulName) + + getInstance(name : NazgulName) : Nazgul {static} + + getName() : NazgulName + } + enum NazgulName { + + ADUNAPHEL {static} + + AKHORAHIL {static} + + DWAR {static} + + HOARMURATH {static} + + JI_INDUR {static} + + KHAMUL {static} + + MURAZOR {static} + + REN {static} + + UVATHA {static} + + valueOf(name : String) : NazgulName {static} + + values() : NazgulName[] {static} + } +} +Nazgul --> "-name" NazgulName +@enduml \ No newline at end of file diff --git a/mute-idiom/etc/mute-idiom.urm.puml b/mute-idiom/etc/mute-idiom.urm.puml new file mode 100644 index 000000000000..d4efc2db120e --- /dev/null +++ b/mute-idiom/etc/mute-idiom.urm.puml @@ -0,0 +1,23 @@ +@startuml +package com.iluwatar.mute { + interface Resource { + } + class App { + + App() + - acquireResource() : Resource {static} + - closeResource(resource : Resource) {static} + + main(args : String[]) {static} + - useOfLoggedMute() {static} + - useOfMute() {static} + - utilizeResource(resource : Resource) {static} + } + class Mute { + - Mute() + + loggedMute(runnable : CheckedRunnable) {static} + + mute(runnable : CheckedRunnable) {static} + } + interface CheckedRunnable { + + run() {abstract} + } +} +@enduml \ No newline at end of file diff --git a/mutex/etc/mutex.urm.puml b/mutex/etc/mutex.urm.puml new file mode 100644 index 000000000000..24ea8363088e --- /dev/null +++ b/mutex/etc/mutex.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.mutex { + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class Mutex { + - owner : Object + + Mutex() + + acquire() + + getOwner() : Object + + release() + } + class Jar { + - beans : int + - lock : Lock + + Jar(beans : int, lock : Lock) + + takeBean() : boolean + } + class App { + + App() + + main(args : String[]) {static} + } +} +Jar --> "-lock" Lock +Mutex ..|> Lock +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects-dom.urm.puml b/naked-objects/etc/naked-objects-dom.urm.puml new file mode 100644 index 000000000000..ea32b57874a4 --- /dev/null +++ b/naked-objects/etc/naked-objects-dom.urm.puml @@ -0,0 +1,84 @@ +@startuml +package domainapp.dom.app.homepage { + class HomePageViewModel { + ~ simpleObjects : SimpleObjects + + HomePageViewModel() + + getObjects() : List + + title() : String + } + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } +} +package domainapp.dom.modules.simple { + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } + class SimpleObject { + - container : DomainObjectContainer + - dnFieldFlags : byte[] {static} + - dnFieldNames : String[] {static} + - dnFieldTypes : Class[] {static} + # dnFlags : byte + - dnInheritedFieldCount : int {static} + - dnPersistableSuperclass : Class {static} + # dnStateManager : StateManager + - name : String + + SimpleObject() + + ___dn$loadClass(className : String) : Class {static} + - __dnFieldFlagsInit() : byte[] {static} + - __dnFieldNamesInit() : String[] {static} + - __dnFieldTypesInit() : Class[] {static} + # __dnGetInheritedFieldCount() : int {static} + - __dnPersistableSuperclassInit() : Class {static} + + compareTo(other : SimpleObject) : int + + default0UpdateName() : String + # dnCopyField(obj : SimpleObject, index : int) + + dnCopyFields(obj : Object, indices : int[]) + + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) + # dnCopyKeyFieldsFromObjectId(oid : Object) + + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) + + dnCopyKeyFieldsToObjectId(oid : Object) + + dnGetExecutionContext() : ExecutionContextReference + # dnGetManagedFieldCount() : int {static} + + dnGetObjectId() : Object + + dnGetTransactionalObjectId() : Object + + dnGetVersion() : Object + + dnGetname() : String + + dnIsDeleted() : boolean + + dnIsDetached() : boolean + + dnIsDirty() : boolean + + dnIsNew() : boolean + + dnIsPersistent() : boolean + + dnIsTransactional() : boolean + + dnMakeDirty(fieldName : String) + + dnNewInstance(sm : StateManager) : Persistable + + dnNewInstance(sm : StateManager, obj : Object) : Persistable + + dnNewObjectIdInstance() : Object + + dnNewObjectIdInstance(key : Object) : Object + # dnPreSerialize() + + dnProvideField(index : int) + + dnProvideFields(indices : int[]) + + dnReplaceField(index : int) + + dnReplaceFields(indices : int[]) + + dnReplaceFlags() + + dnReplaceStateManager(sm : StateManager) + + dnSetname(name : String) + - dnSuperClone() : Object + + getName() : String + + getVersionSequence() : Long + + setName(val : String) + + title() : TranslatableString + + updateName(name : String) : SimpleObject + + validateUpdateName(name : String) : TranslatableString + } +} +HomePageViewModel --> "-simpleObjects" SimpleObjects +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects-fixture.urm.puml b/naked-objects/etc/naked-objects-fixture.urm.puml new file mode 100644 index 000000000000..65c44410ae92 --- /dev/null +++ b/naked-objects/etc/naked-objects-fixture.urm.puml @@ -0,0 +1,92 @@ +@startuml +package domainapp.dom.app.homepage { + class HomePageViewModel { + ~ simpleObjects : SimpleObjects + + HomePageViewModel() + + getObjects() : List + + title() : String + } + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } +} +package domainapp.dom.modules.simple { + class SimpleObject { + - container : DomainObjectContainer + - dnFieldFlags : byte[] {static} + - dnFieldNames : String[] {static} + - dnFieldTypes : Class[] {static} + # dnFlags : byte + - dnInheritedFieldCount : int {static} + - dnPersistableSuperclass : Class {static} + # dnStateManager : StateManager + - name : String + + SimpleObject() + + ___dn$loadClass(className : String) : Class {static} + - __dnFieldFlagsInit() : byte[] {static} + - __dnFieldNamesInit() : String[] {static} + - __dnFieldTypesInit() : Class[] {static} + # __dnGetInheritedFieldCount() : int {static} + - __dnPersistableSuperclassInit() : Class {static} + + compareTo(other : SimpleObject) : int + + default0UpdateName() : String + # dnCopyField(obj : SimpleObject, index : int) + + dnCopyFields(obj : Object, indices : int[]) + + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) + # dnCopyKeyFieldsFromObjectId(oid : Object) + + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) + + dnCopyKeyFieldsToObjectId(oid : Object) + + dnGetExecutionContext() : ExecutionContextReference + # dnGetManagedFieldCount() : int {static} + + dnGetObjectId() : Object + + dnGetTransactionalObjectId() : Object + + dnGetVersion() : Object + + dnGetname() : String + + dnIsDeleted() : boolean + + dnIsDetached() : boolean + + dnIsDirty() : boolean + + dnIsNew() : boolean + + dnIsPersistent() : boolean + + dnIsTransactional() : boolean + + dnMakeDirty(fieldName : String) + + dnNewInstance(sm : StateManager) : Persistable + + dnNewInstance(sm : StateManager, obj : Object) : Persistable + + dnNewObjectIdInstance() : Object + + dnNewObjectIdInstance(key : Object) : Object + # dnPreSerialize() + + dnProvideField(index : int) + + dnProvideFields(indices : int[]) + + dnReplaceField(index : int) + + dnReplaceFields(indices : int[]) + + dnReplaceFlags() + + dnReplaceStateManager(sm : StateManager) + + dnSetname(name : String) + - dnSuperClone() : Object + + getName() : String + + getVersionSequence() : Long + + setName(val : String) + + title() : TranslatableString + + updateName(name : String) : SimpleObject + + validateUpdateName(name : String) : TranslatableString + } + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } +} +package domainapp.fixture { + class DomainAppFixturesProvider { + + DomainAppFixturesProvider() + + getSpecification() : FixtureScriptsSpecification + } +} +DomainAppFixturesProvider --+ FixtureScripts +DomainAppFixturesProvider --+ FixtureScriptsSpecification +HomePageViewModel --> "-simpleObjects" SimpleObjects +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects-integtests.urm.puml b/naked-objects/etc/naked-objects-integtests.urm.puml new file mode 100644 index 000000000000..1f2dfc4c2321 --- /dev/null +++ b/naked-objects/etc/naked-objects-integtests.urm.puml @@ -0,0 +1,92 @@ +@startuml +package domainapp.dom.app.homepage { + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } + class HomePageViewModel { + ~ simpleObjects : SimpleObjects + + HomePageViewModel() + + getObjects() : List + + title() : String + } +} +package domainapp.dom.modules.simple { + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } + class SimpleObject { + - container : DomainObjectContainer + - dnFieldFlags : byte[] {static} + - dnFieldNames : String[] {static} + - dnFieldTypes : Class[] {static} + # dnFlags : byte + - dnInheritedFieldCount : int {static} + - dnPersistableSuperclass : Class {static} + # dnStateManager : StateManager + - name : String + + SimpleObject() + + ___dn$loadClass(className : String) : Class {static} + - __dnFieldFlagsInit() : byte[] {static} + - __dnFieldNamesInit() : String[] {static} + - __dnFieldTypesInit() : Class[] {static} + # __dnGetInheritedFieldCount() : int {static} + - __dnPersistableSuperclassInit() : Class {static} + + compareTo(other : SimpleObject) : int + + default0UpdateName() : String + # dnCopyField(obj : SimpleObject, index : int) + + dnCopyFields(obj : Object, indices : int[]) + + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) + # dnCopyKeyFieldsFromObjectId(oid : Object) + + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) + + dnCopyKeyFieldsToObjectId(oid : Object) + + dnGetExecutionContext() : ExecutionContextReference + # dnGetManagedFieldCount() : int {static} + + dnGetObjectId() : Object + + dnGetTransactionalObjectId() : Object + + dnGetVersion() : Object + + dnGetname() : String + + dnIsDeleted() : boolean + + dnIsDetached() : boolean + + dnIsDirty() : boolean + + dnIsNew() : boolean + + dnIsPersistent() : boolean + + dnIsTransactional() : boolean + + dnMakeDirty(fieldName : String) + + dnNewInstance(sm : StateManager) : Persistable + + dnNewInstance(sm : StateManager, obj : Object) : Persistable + + dnNewObjectIdInstance() : Object + + dnNewObjectIdInstance(key : Object) : Object + # dnPreSerialize() + + dnProvideField(index : int) + + dnProvideFields(indices : int[]) + + dnReplaceField(index : int) + + dnReplaceFields(indices : int[]) + + dnReplaceFlags() + + dnReplaceStateManager(sm : StateManager) + + dnSetname(name : String) + - dnSuperClone() : Object + + getName() : String + + getVersionSequence() : Long + + setName(val : String) + + title() : TranslatableString + + updateName(name : String) : SimpleObject + + validateUpdateName(name : String) : TranslatableString + } +} +package domainapp.fixture { + class DomainAppFixturesProvider { + + DomainAppFixturesProvider() + + getSpecification() : FixtureScriptsSpecification + } +} +DomainAppFixturesProvider --+ FixtureScripts +DomainAppFixturesProvider --+ FixtureScriptsSpecification +HomePageViewModel --> "-simpleObjects" SimpleObjects +@enduml \ No newline at end of file diff --git a/null-object/etc/null-object.urm.puml b/null-object/etc/null-object.urm.puml new file mode 100644 index 000000000000..d0b2936c59ba --- /dev/null +++ b/null-object/etc/null-object.urm.puml @@ -0,0 +1,40 @@ +@startuml +package com.iluwatar.nullobject { + class NullNode { + - instance : NullNode {static} + - NullNode() + + getInstance() : NullNode {static} + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } + interface Node { + + getLeft() : Node {abstract} + + getName() : String {abstract} + + getRight() : Node {abstract} + + getTreeSize() : int {abstract} + + walk() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class NodeImpl { + - left : Node + - name : String + - right : Node + + NodeImpl(name : String, left : Node, right : Node) + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } +} +NullNode --> "-instance" NullNode +NodeImpl --> "-left" Node +NullNode ..|> Node +NodeImpl ..|> Node +@enduml \ No newline at end of file diff --git a/object-pool/etc/object-pool.urm.puml b/object-pool/etc/object-pool.urm.puml new file mode 100644 index 000000000000..9df1081d3355 --- /dev/null +++ b/object-pool/etc/object-pool.urm.puml @@ -0,0 +1,29 @@ +@startuml +package com.iluwatar.object.pool { + class Oliphaunt { + - counter : int {static} + - id : int + + Oliphaunt() + + getId() : int + + toString() : String + } + class OliphauntPool { + + OliphauntPool() + # create() : Oliphaunt + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class ObjectPool { + - available : HashSet + - inUse : HashSet + + ObjectPool() + + checkIn(instance : T) + + checkOut() : T + # create() : T {abstract} + + toString() : String + } +} +OliphauntPool --|> ObjectPool +@enduml \ No newline at end of file diff --git a/observer/etc/observer.urm.puml b/observer/etc/observer.urm.puml new file mode 100644 index 000000000000..33485d7319ee --- /dev/null +++ b/observer/etc/observer.urm.puml @@ -0,0 +1,73 @@ +@startuml +package com.iluwatar.observer { + class Orcs { + + Orcs() + + update(currentWeather : WeatherType) + } + class Hobbits { + + Hobbits() + + update(currentWeather : WeatherType) + } + class Weather { + - currentWeather : WeatherType + - observers : List + + Weather() + + addObserver(obs : WeatherObserver) + - notifyObservers() + + removeObserver(obs : WeatherObserver) + + timePasses() + } + class App { + + App() + + main(args : String[]) {static} + } + interface WeatherObserver { + + update(WeatherType) {abstract} + } + enum WeatherType { + + COLD {static} + + RAINY {static} + + SUNNY {static} + + WINDY {static} + + toString() : String + + valueOf(name : String) : WeatherType {static} + + values() : WeatherType[] {static} + } +} +package com.iluwatar.observer.generic { + class GOrcs { + + GOrcs() + + update(weather : GWeather, weatherType : WeatherType) + } + interface Race { + } + abstract class Observable, A> { + # observers : List> + + Observable, A>() + + addObserver(observer : O extends Observer) + + notifyObservers(argument : A) + + removeObserver(observer : O extends Observer) + } + class GWeather { + - currentWeather : WeatherType + + GWeather() + + timePasses() + } + interface Observer, O extends Observer, A> { + + update(S extends Observable, A) {abstract} + } + class GHobbits { + + GHobbits() + + update(weather : GWeather, weatherType : WeatherType) + } +} +Weather --> "-currentWeather" WeatherType +GWeather --> "-currentWeather" WeatherType +Weather --> "-observers" WeatherObserver +GOrcs ..|> Race +Orcs ..|> WeatherObserver +Hobbits ..|> WeatherObserver +Race --|> Observer +GWeather --|> Observable +GHobbits ..|> Race +@enduml \ No newline at end of file diff --git a/page-object/etc/page-object.urm.puml b/page-object/etc/page-object.urm.puml new file mode 100644 index 000000000000..735cf2889bcb --- /dev/null +++ b/page-object/etc/page-object.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.pageobject { + class App { + - App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/poison-pill/etc/poison-pill.urm.puml b/poison-pill/etc/poison-pill.urm.puml new file mode 100644 index 000000000000..58f9eb93794a --- /dev/null +++ b/poison-pill/etc/poison-pill.urm.puml @@ -0,0 +1,72 @@ +@startuml +package com.iluwatar.poison.pill { + interface Message { + + POISON_PILL : Message {static} + + addHeader(Headers, String) {abstract} + + getBody() : String {abstract} + + getHeader(Headers) : String {abstract} + + getHeaders() : Map {abstract} + + setBody(String) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class SimpleMessage { + - body : String + - headers : Map + + SimpleMessage() + + addHeader(header : Headers, value : String) + + getBody() : String + + getHeader(header : Headers) : String + + getHeaders() : Map + + setBody(body : String) + } + class SimpleMessageQueue { + - queue : BlockingQueue + + SimpleMessageQueue(bound : int) + + put(msg : Message) + + take() : Message + } + class Producer { + - isStopped : boolean + - name : String + - queue : MqPublishPoint + + Producer(name : String, queue : MqPublishPoint) + + send(body : String) + + stop() + } + interface MqSubscribePoint { + + take() : Message {abstract} + } + class Consumer { + - name : String + - queue : MqSubscribePoint + + Consumer(name : String, queue : MqSubscribePoint) + + consume() + } + interface MessageQueue { + } + interface MqPublishPoint { + + put(Message) {abstract} + } + enum Headers { + + DATE {static} + + SENDER {static} + + valueOf(name : String) : Headers {static} + + values() : Headers[] {static} + } +} +SimpleMessageQueue --> "-queue" Message +Headers ..+ Message +Consumer --> "-queue" MqSubscribePoint +Producer --> "-queue" MqPublishPoint +SimpleMessage --+ Message +Producer --+ Message +Message --> "-POISON_PILL" Message +Consumer --+ Message +SimpleMessage ..|> Message +SimpleMessageQueue ..|> MessageQueue +MessageQueue --|> MqPublishPoint +MessageQueue --|> MqSubscribePoint +@enduml \ No newline at end of file diff --git a/private-class-data/etc/private-class-data.urm.puml b/private-class-data/etc/private-class-data.urm.puml new file mode 100644 index 000000000000..0edc2c1a835c --- /dev/null +++ b/private-class-data/etc/private-class-data.urm.puml @@ -0,0 +1,34 @@ +@startuml +package com.iluwatar.privateclassdata { + class Stew { + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + + taste() + } + class App { + + App() + + main(args : String[]) {static} + } + class StewData { + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + StewData(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + getNumCarrots() : int + + getNumMeat() : int + + getNumPeppers() : int + + getNumPotatoes() : int + } + class ImmutableStew { + - data : StewData + + ImmutableStew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + } +} +ImmutableStew --> "-data" StewData +@enduml \ No newline at end of file diff --git a/producer-consumer/etc/producer-consumer.urm.puml b/producer-consumer/etc/producer-consumer.urm.puml new file mode 100644 index 000000000000..9a67485523df --- /dev/null +++ b/producer-consumer/etc/producer-consumer.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.producer.consumer { + class Producer { + - itemId : int + - name : String + - queue : ItemQueue + + Producer(name : String, queue : ItemQueue) + + produce() + } + class ItemQueue { + - queue : BlockingQueue + + ItemQueue() + + put(item : Item) + + take() : Item + } + class Item { + - id : int + - producer : String + + Item(producer : String, id : int) + + getId() : int + + getProducer() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class Consumer { + - name : String + - queue : ItemQueue + + Consumer(name : String, queue : ItemQueue) + + consume() + } +} +Consumer --> "-queue" ItemQueue +Producer --> "-queue" ItemQueue +ItemQueue --> "-queue" Item +@enduml \ No newline at end of file diff --git a/property/etc/property.urm.puml b/property/etc/property.urm.puml new file mode 100644 index 000000000000..7c90edccc847 --- /dev/null +++ b/property/etc/property.urm.puml @@ -0,0 +1,54 @@ +@startuml +package com.iluwatar.property { + class Character { + - name : String + - properties : Map + - prototype : Prototype + - type : Type + + Character() + + Character(name : String, prototype : Character) + + Character(type : Type, prototype : Prototype) + + get(stat : Stats) : Integer + + has(stat : Stats) : boolean + + name() : String + + remove(stat : Stats) + + set(stat : Stats, val : Integer) + + toString() : String + + type() : Type + } + interface Prototype { + + get(Stats) : Integer {abstract} + + has(Stats) : boolean {abstract} + + remove(Stats) {abstract} + + set(Stats, Integer) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + enum Stats { + + AGILITY {static} + + ARMOR {static} + + ATTACK_POWER {static} + + ENERGY {static} + + INTELLECT {static} + + RAGE {static} + + SPIRIT {static} + + STRENGTH {static} + + valueOf(name : String) : Stats {static} + + values() : Stats[] {static} + } + enum Type { + + MAGE {static} + + ROGUE {static} + + WARRIOR {static} + + valueOf(name : String) : Type {static} + + values() : Type[] {static} + } +} +App --+ Character +Character --> "-prototype" Prototype +Character --> "-type" Type +Type ..+ Character +Character ..|> Prototype +@enduml \ No newline at end of file diff --git a/prototype/etc/prototype.urm.puml b/prototype/etc/prototype.urm.puml new file mode 100644 index 000000000000..7bfc00e15625 --- /dev/null +++ b/prototype/etc/prototype.urm.puml @@ -0,0 +1,81 @@ +@startuml +package com.iluwatar.prototype { + interface HeroFactory { + + createBeast() : Beast {abstract} + + createMage() : Mage {abstract} + + createWarlord() : Warlord {abstract} + } + class OrcBeast { + + OrcBeast() + + clone() : Beast + + toString() : String + } + abstract class Mage { + + Mage() + + clone() : Mage {abstract} + } + class HeroFactoryImpl { + - beast : Beast + - mage : Mage + - warlord : Warlord + + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) + + createBeast() : Beast + + createMage() : Mage + + createWarlord() : Warlord + } + class ElfMage { + + ElfMage() + + clone() : Mage + + toString() : String + } + abstract class Prototype { + + Prototype() + + clone() : Object {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Warlord { + + Warlord() + + clone() : Warlord {abstract} + } + class OrcWarlord { + + OrcWarlord() + + clone() : Warlord + + toString() : String + } + class ElfWarlord { + + ElfWarlord() + + clone() : Warlord + + toString() : String + } + abstract class Beast { + + Beast() + + clone() : Beast {abstract} + } + class OrcMage { + + OrcMage() + + clone() : Mage + + toString() : String + } + class ElfBeast { + + ElfBeast() + + clone() : Beast + + toString() : String + } +} +HeroFactoryImpl --> "-beast" Beast +HeroFactoryImpl --> "-warlord" Warlord +HeroFactoryImpl --> "-mage" Mage +OrcBeast --|> Beast +Mage --|> Prototype +HeroFactoryImpl ..|> HeroFactory +ElfMage --|> Mage +Warlord --|> Prototype +OrcWarlord --|> Warlord +ElfWarlord --|> Warlord +Beast --|> Prototype +OrcMage --|> Mage +ElfBeast --|> Beast +@enduml \ No newline at end of file diff --git a/proxy/etc/proxy.urm.puml b/proxy/etc/proxy.urm.puml new file mode 100644 index 000000000000..4203174de6e6 --- /dev/null +++ b/proxy/etc/proxy.urm.puml @@ -0,0 +1,24 @@ +@startuml +package com.iluwatar.proxy { + class WizardTower { + + WizardTower() + + enter(wizard : Wizard) + } + class App { + + App() + + main(args : String[]) {static} + } + class WizardTowerProxy { + - NUM_WIZARDS_ALLOWED : int {static} + - numWizards : int + + WizardTowerProxy() + + enter(wizard : Wizard) + } + class Wizard { + - name : String + + Wizard(name : String) + + toString() : String + } +} +WizardTowerProxy --|> WizardTower +@enduml \ No newline at end of file diff --git a/publish-subscribe/etc/publish-subscribe.urm.puml b/publish-subscribe/etc/publish-subscribe.urm.puml new file mode 100644 index 000000000000..1272f1f6db29 --- /dev/null +++ b/publish-subscribe/etc/publish-subscribe.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.publish.subscribe { + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/reactor/etc/reactor.urm.puml b/reactor/etc/reactor.urm.puml new file mode 100644 index 000000000000..302f2663c3a2 --- /dev/null +++ b/reactor/etc/reactor.urm.puml @@ -0,0 +1,151 @@ +@startuml +package com.iluwatar.reactor.app { + ~class TcpLoggingClient { + - clientName : String + - serverPort : int + + TcpLoggingClient(clientName : String, serverPort : int) + + run() + - sendLogRequests(writer : PrintWriter, inputStream : InputStream) + } + ~class UdpLoggingClient { + - clientName : String + - remoteAddress : InetSocketAddress + + UdpLoggingClient(clientName : String, port : int) + + run() + } + class LoggingHandler { + - ACK : byte[] {static} + + LoggingHandler() + - doLogging(data : ByteBuffer) {static} + + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} + - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} + } + class AppClient { + - service : ExecutorService + + AppClient() + - artificialDelayOf(millis : long) {static} + + main(args : String[]) {static} + + start() + + stop() + } + class App { + - channels : List + - dispatcher : Dispatcher + - reactor : NioReactor + + App(dispatcher : Dispatcher) + + main(args : String[]) {static} + + start() + + stop() + - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + } +} +package com.iluwatar.reactor.framework { + interface Dispatcher { + + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} + + stop() {abstract} + } + class SameThreadDispatcher { + + SameThreadDispatcher() + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + class ThreadPoolDispatcher { + - executorService : ExecutorService + + ThreadPoolDispatcher(poolSize : int) + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + interface ChannelHandler { + + handleChannelRead(AbstractNioChannel, Object, SelectionKey) {abstract} + } + class NioDatagramChannel { + - port : int + + NioDatagramChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : DatagramChannel + + read(key : SelectionKey) : DatagramPacket + + write(data : Object, key : SelectionKey) + } + class DatagramPacket { + - data : ByteBuffer + - receiver : SocketAddress + - sender : SocketAddress + + DatagramPacket(data : ByteBuffer) + + getData() : ByteBuffer + + getReceiver() : SocketAddress + + getSender() : SocketAddress + + setReceiver(receiver : SocketAddress) + + setSender(sender : SocketAddress) + } + abstract class AbstractNioChannel { + - channel : SelectableChannel + - channelToPendingWrites : Map> + - handler : ChannelHandler + - reactor : NioReactor + + AbstractNioChannel(handler : ChannelHandler, channel : SelectableChannel) + + bind() {abstract} + # doWrite(Object, SelectionKey) {abstract} + ~ flush(key : SelectionKey) + + getHandler() : ChannelHandler + + getInterestedOps() : int {abstract} + + getJavaChannel() : SelectableChannel + + read(SelectionKey) : Object {abstract} + ~ setReactor(reactor : NioReactor) + + write(data : Object, key : SelectionKey) + } + class NioServerSocketChannel { + - port : int + + NioServerSocketChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : ServerSocketChannel + + read(key : SelectionKey) : ByteBuffer + } + class NioReactor { + - dispatcher : Dispatcher + - pendingCommands : Queue + - reactorMain : ExecutorService + - selector : Selector + + NioReactor(dispatcher : Dispatcher) + + changeOps(key : SelectionKey, interestedOps : int) + - dispatchReadEvent(key : SelectionKey, readObject : Object) + - eventLoop() + - onChannelAcceptable(key : SelectionKey) + - onChannelReadable(key : SelectionKey) + - onChannelWritable(key : SelectionKey) {static} + - processKey(key : SelectionKey) + - processPendingCommands() + + registerChannel(channel : AbstractNioChannel) : NioReactor + + start() + + stop() + } + ~class ChangeKeyOpsCommand { + - interestedOps : int + - key : SelectionKey + + ChangeKeyOpsCommand(this$0 : NioReactor, key : SelectionKey, interestedOps : int) + + run() + + toString() : String + } +} +AbstractNioChannel --> "-handler" ChannelHandler +UdpLoggingClient ..+ AppClient +AbstractNioChannel --> "-reactor" NioReactor +TcpLoggingClient ..+ AppClient +NioReactor --> "-dispatcher" Dispatcher +App --> "-reactor" NioReactor +App --> "-channels" AbstractNioChannel +DatagramPacket ..+ NioDatagramChannel +ChangeKeyOpsCommand --+ NioReactor +App --> "-dispatcher" Dispatcher +LoggingHandler --+ NioDatagramChannel +SameThreadDispatcher ..|> Dispatcher +LoggingHandler ..|> ChannelHandler +ThreadPoolDispatcher ..|> Dispatcher +NioDatagramChannel --|> AbstractNioChannel +NioServerSocketChannel --|> AbstractNioChannel +@enduml \ No newline at end of file diff --git a/reader-writer-lock/etc/reader-writer-lock.urm.puml b/reader-writer-lock/etc/reader-writer-lock.urm.puml new file mode 100644 index 000000000000..22b303cf50a2 --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.urm.puml @@ -0,0 +1,58 @@ +@startuml +package com.iluwatar.reader.writer.lock { + -class ReadLock { + - ReadLock(ReaderWriterLock) + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class Writer { + - name : String + - writeLock : Lock + + Writer(name : String, writeLock : Lock) + + run() + + write() + } + class ReaderWriterLock { + - currentReaderCount : int + - globalMutex : Set + - readerLock : ReadLock + - readerMutex : Object + - writerLock : WriteLock + + ReaderWriterLock() + - doesReaderOwnThisLock() : boolean + - doesWriterOwnThisLock() : boolean + - isLockFree() : boolean + + readLock() : Lock + - waitUninterruptibly(o : Object) {static} + + writeLock() : Lock + } + -class WriteLock { + - WriteLock(ReaderWriterLock) + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class App { + + App() + + main(args : String[]) {static} + } + class Reader { + - name : String + - readLock : Lock + + Reader(name : String, readLock : Lock) + + read() + + run() + } +} +ReadLock --+ ReaderWriterLock +ReaderWriterLock --> "-readerLock" ReadLock +ReaderWriterLock --> "-writerLock" WriteLock +WriteLock --+ ReaderWriterLock +@enduml \ No newline at end of file diff --git a/repository/etc/repository.urm.puml b/repository/etc/repository.urm.puml new file mode 100644 index 000000000000..49a2c8fdc72b --- /dev/null +++ b/repository/etc/repository.urm.puml @@ -0,0 +1,56 @@ +@startuml +package com.iluwatar.repository { + class App { + + App() + + main(args : String[]) {static} + } + class Person { + - age : int + - id : Long + - name : String + - surname : String + + Person() + + Person(name : String, surname : String, age : int) + + equals(obj : Object) : boolean + + getAge() : int + + getId() : Long + + getName() : String + + getSurname() : String + + hashCode() : int + + setAge(age : int) + + setId(id : Long) + + setName(name : String) + + setSurname(surname : String) + + toString() : String + } + class AgeBetweenSpec { + - from : int + - to : int + + AgeBetweenSpec(from : int, to : int) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class AppConfig { + + AppConfig() + + dataSource() : DataSource + + entityManagerFactory() : LocalContainerEntityManagerFactoryBean + - jpaProperties() : Properties {static} + + main(args : String[]) {static} + + transactionManager() : JpaTransactionManager + } + interface PersonRepository { + + findByName(String) : Person {abstract} + } + class NameEqualSpec { + + name : String + + NameEqualSpec(name : String) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class PersonSpecifications { + + PersonSpecifications() + } +} +App --+ PersonSpecifications +AppConfig --+ PersonSpecifications +NameEqualSpec ..+ PersonSpecifications +AgeBetweenSpec ..+ PersonSpecifications +@enduml \ No newline at end of file diff --git a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml new file mode 100644 index 000000000000..847b716a02db --- /dev/null +++ b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml @@ -0,0 +1,16 @@ +@startuml +package com.iluwatar.resource.acquisition.is.initialization { + class App { + + App() + + main(args : String[]) {static} + } + class TreasureChest { + + TreasureChest() + + close() + } + class SlidingDoor { + + SlidingDoor() + + close() + } +} +@enduml \ No newline at end of file diff --git a/semaphore/etc/semaphore.urm.puml b/semaphore/etc/semaphore.urm.puml new file mode 100644 index 000000000000..f85fff92175d --- /dev/null +++ b/semaphore/etc/semaphore.urm.puml @@ -0,0 +1,58 @@ +@startuml +package com.iluwatar.semaphore { + class FruitShop { + - available : boolean[] + - bowls : FruitBowl[] + - semaphore : Semaphore + + FruitShop() + + countFruit() : int + + returnBowl(bowl : FruitBowl) + + takeBowl() : FruitBowl + } + class FruitBowl { + - fruit : ArrayList + + FruitBowl() + + countFruit() : int + + put(f : Fruit) + + take() : Fruit + + toString() : String + } + class Fruit { + - type : FruitType + + Fruit(type : FruitType) + + getType() : FruitType + + toString() : String + } + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class Semaphore { + - counter : int + - licenses : int + + Semaphore(licenses : int) + + acquire() + + getAvailableLicenses() : int + + getNumLicenses() : int + + release() + } + enum FruitType { + + APPLE {static} + + LEMON {static} + + ORANGE {static} + + valueOf(name : String) : FruitType {static} + + values() : FruitType[] {static} + } +} +FruitShop --+ Fruit +Fruit --> "-type" FruitType +FruitType ..+ Fruit +FruitBowl --+ Fruit +FruitBowl --> "-fruit" Fruit +FruitShop --> "-semaphore" Semaphore +Semaphore ..|> Lock +@enduml \ No newline at end of file diff --git a/servant/etc/servant.urm.puml b/servant/etc/servant.urm.puml new file mode 100644 index 000000000000..48d48bd842d0 --- /dev/null +++ b/servant/etc/servant.urm.puml @@ -0,0 +1,55 @@ +@startuml +package com.iluwatar.servant { + class King { + - complimentReceived : boolean + - isDrunk : boolean + - isHappy : boolean + - isHungry : boolean + + King() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + } + ~interface Royalty { + + changeMood() {abstract} + + getDrink() {abstract} + + getFed() {abstract} + + getMood() : boolean {abstract} + + receiveCompliments() {abstract} + } + class Servant { + + name : String + + Servant(name : String) + + checkIfYouWillBeHanged(tableGuests : List) : boolean + + feed(r : Royalty) + + giveCompliments(r : Royalty) + + giveWine(r : Royalty) + } + class Queen { + - complimentReceived : boolean + - isDrunk : boolean + - isFlirty : boolean + - isHappy : boolean + - isHungry : boolean + + Queen() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + + setFlirtiness(f : boolean) + } + class App { + ~ jenkins : Servant {static} + ~ travis : Servant {static} + + App() + + main(args : String[]) {static} + + scenario(servant : Servant, compliment : int) {static} + } +} +App --> "-jenkins" Servant +King ..|> Royalty +Queen ..|> Royalty +@enduml \ No newline at end of file diff --git a/service-layer/etc/service-layer.urm.puml b/service-layer/etc/service-layer.urm.puml new file mode 100644 index 000000000000..c67ffa645560 --- /dev/null +++ b/service-layer/etc/service-layer.urm.puml @@ -0,0 +1,158 @@ +@startuml +package com.iluwatar.servicelayer.hibernate { + class HibernateUtil { + - sessionFactory : SessionFactory {static} + - HibernateUtil() + + dropSession() {static} + + getSessionFactory() : SessionFactory {static} + } +} +package com.iluwatar.servicelayer.common { + abstract class BaseEntity { + - version : Long + + BaseEntity() + + getId() : Long {abstract} + + getName() : String {abstract} + + setId(Long) {abstract} + + setName(String) {abstract} + } + interface Dao { + + delete(E extends BaseEntity) {abstract} + + find(Long) : E extends BaseEntity {abstract} + + findAll() : List {abstract} + + merge(E extends BaseEntity) : E extends BaseEntity {abstract} + + persist(E extends BaseEntity) {abstract} + } + abstract class DaoBaseImpl { + # persistentClass : Class + + DaoBaseImpl() + + delete(entity : E extends BaseEntity) + + find(id : Long) : E extends BaseEntity + + findAll() : List + # getSession() : Session + + merge(entity : E extends BaseEntity) : E extends BaseEntity + + persist(entity : E extends BaseEntity) + } +} +package com.iluwatar.servicelayer.magic { + interface MagicService { + + findAllSpellbooks() : List {abstract} + + findAllSpells() : List {abstract} + + findAllWizards() : List {abstract} + + findWizardsWithSpell(String) : List {abstract} + + findWizardsWithSpellbook(String) : List {abstract} + } + class MagicServiceImpl { + - spellDao : SpellDao + - spellbookDao : SpellbookDao + - wizardDao : WizardDao + + MagicServiceImpl(wizardDao : WizardDao, spellbookDao : SpellbookDao, spellDao : SpellDao) + + findAllSpellbooks() : List + + findAllSpells() : List + + findAllWizards() : List + + findWizardsWithSpell(name : String) : List + + findWizardsWithSpellbook(name : String) : List + } +} +package com.iluwatar.servicelayer.wizard { + class Wizard { + - id : Long + - name : String + - spellbooks : Set + + Wizard() + + Wizard(name : String) + + addSpellbook(spellbook : Spellbook) + + getId() : Long + + getName() : String + + getSpellbooks() : Set + + setId(id : Long) + + setName(name : String) + + setSpellbooks(spellbooks : Set) + + toString() : String + } + class WizardDaoImpl { + + WizardDaoImpl() + + findByName(name : String) : Wizard + } + interface WizardDao { + + findByName(String) : Wizard {abstract} + } +} +package com.iluwatar.servicelayer.app { + class App { + + App() + + initData() {static} + + main(args : String[]) {static} + + queryData() {static} + } +} +package com.iluwatar.servicelayer.spell { + class SpellDaoImpl { + + SpellDaoImpl() + + findByName(name : String) : Spell + } + class Spell { + - id : Long + - name : String + - spellbook : Spellbook + + Spell() + + Spell(name : String) + + getId() : Long + + getName() : String + + getSpellbook() : Spellbook + + setId(id : Long) + + setName(name : String) + + setSpellbook(spellbook : Spellbook) + + toString() : String + } + interface SpellDao { + + findByName(String) : Spell {abstract} + } +} +package com.iluwatar.servicelayer.spellbook { + interface SpellbookDao { + + findByName(String) : Spellbook {abstract} + } + class Spellbook { + - id : Long + - name : String + - spells : Set + - wizards : Set + + Spellbook() + + Spellbook(name : String) + + addSpell(spell : Spell) + + getId() : Long + + getName() : String + + getSpells() : Set + + getWizards() : Set + + setId(id : Long) + + setName(name : String) + + setSpells(spells : Set) + + setWizards(wizards : Set) + + toString() : String + } + class SpellbookDaoImpl { + + SpellbookDaoImpl() + + findByName(name : String) : Spellbook + } +} +MagicServiceImpl --> "-wizardDao" WizardDao +MagicServiceImpl --> "-spellbookDao" SpellbookDao +MagicServiceImpl --> "-spellDao" SpellDao +Spellbook --> "-spells" Spell +Spellbook --> "-wizards" Wizard +Wizard --|> BaseEntity +SpellbookDao --|> Dao +SpellDaoImpl ..|> SpellDao +SpellDaoImpl --|> DaoBaseImpl +MagicServiceImpl ..|> MagicService +DaoBaseImpl ..|> Dao +WizardDaoImpl ..|> WizardDao +WizardDaoImpl --|> DaoBaseImpl +Spellbook --|> BaseEntity +SpellbookDaoImpl ..|> SpellbookDao +SpellbookDaoImpl --|> DaoBaseImpl +Spell --|> BaseEntity +WizardDao --|> Dao +SpellDao --|> Dao +@enduml \ No newline at end of file diff --git a/service-locator/etc/service-locator.urm.puml b/service-locator/etc/service-locator.urm.puml new file mode 100644 index 000000000000..085b05b285e3 --- /dev/null +++ b/service-locator/etc/service-locator.urm.puml @@ -0,0 +1,38 @@ +@startuml +package com.iluwatar.servicelocator { + interface Service { + + execute() {abstract} + + getId() : int {abstract} + + getName() : String {abstract} + } + class InitContext { + + InitContext() + + lookup(serviceName : String) : Object + } + class ServiceLocator { + - serviceCache : ServiceCache {static} + - ServiceLocator() + + getService(serviceJndiName : String) : Service {static} + } + class ServiceCache { + - serviceCache : Map + + ServiceCache() + + addService(newService : Service) + + getService(serviceName : String) : Service + } + class App { + + App() + + main(args : String[]) {static} + } + class ServiceImpl { + - id : int + - serviceName : String + + ServiceImpl(serviceName : String) + + execute() + + getId() : int + + getName() : String + } +} +ServiceLocator --> "-serviceCache" ServiceCache +ServiceImpl ..|> Service +@enduml \ No newline at end of file diff --git a/singleton/etc/singleton.urm.puml b/singleton/etc/singleton.urm.puml new file mode 100644 index 000000000000..f5ec198794a0 --- /dev/null +++ b/singleton/etc/singleton.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.singleton { + class ThreadSafeLazyLoadedIvoryTower { + - instance : ThreadSafeLazyLoadedIvoryTower {static} + - ThreadSafeLazyLoadedIvoryTower() + + getInstance() : ThreadSafeLazyLoadedIvoryTower {static} + } + -class HelperHolder { + + INSTANCE : InitializingOnDemandHolderIdiom {static} + - HelperHolder() + } + class App { + + App() + + main(args : String[]) {static} + } + class ThreadSafeDoubleCheckLocking { + - instance : ThreadSafeDoubleCheckLocking {static} + - ThreadSafeDoubleCheckLocking() + + getInstance() : ThreadSafeDoubleCheckLocking {static} + } + class InitializingOnDemandHolderIdiom { + - InitializingOnDemandHolderIdiom() + + getInstance() : InitializingOnDemandHolderIdiom {static} + } + class IvoryTower { + - INSTANCE : IvoryTower {static} + - IvoryTower() + + getInstance() : IvoryTower {static} + } + enum EnumIvoryTower { + + INSTANCE {static} + + toString() : String + + valueOf(name : String) : EnumIvoryTower {static} + + values() : EnumIvoryTower[] {static} + } +} +IvoryTower --> "-INSTANCE" IvoryTower +ThreadSafeDoubleCheckLocking --> "-instance" ThreadSafeDoubleCheckLocking +ThreadSafeLazyLoadedIvoryTower --> "-instance" ThreadSafeLazyLoadedIvoryTower +HelperHolder ..+ InitializingOnDemandHolderIdiom +HelperHolder --> "-INSTANCE" InitializingOnDemandHolderIdiom +@enduml \ No newline at end of file diff --git a/specification/etc/specification.urm.puml b/specification/etc/specification.urm.puml new file mode 100644 index 000000000000..0009a1bcd18e --- /dev/null +++ b/specification/etc/specification.urm.puml @@ -0,0 +1,106 @@ +@startuml +package com.iluwatar.specification.creature { + class Goblin { + + Goblin() + } + interface Creature { + + getColor() : Color {abstract} + + getMovement() : Movement {abstract} + + getName() : String {abstract} + + getSize() : Size {abstract} + } + class Troll { + + Troll() + } + abstract class AbstractCreature { + - color : Color + - movement : Movement + - name : String + - size : Size + + AbstractCreature(name : String, size : Size, movement : Movement, color : Color) + + getColor() : Color + + getMovement() : Movement + + getName() : String + + getSize() : Size + + toString() : String + } + class Shark { + + Shark() + } + class KillerBee { + + KillerBee() + } + class Octopus { + + Octopus() + } + class Dragon { + + Dragon() + } +} +package com.iluwatar.specification.property { + enum Color { + + DARK {static} + + GREEN {static} + + LIGHT {static} + + RED {static} + - title : String + + toString() : String + + valueOf(name : String) : Color {static} + + values() : Color[] {static} + } + enum Movement { + + FLYING {static} + + SWIMMING {static} + + WALKING {static} + - title : String + + toString() : String + + valueOf(name : String) : Movement {static} + + values() : Movement[] {static} + } + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } +} +package com.iluwatar.specification.app { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.specification.selector { + class SizeSelector { + - s : Size + + SizeSelector(s : Size) + + test(t : Creature) : boolean + } + class ColorSelector { + - c : Color + + ColorSelector(c : Color) + + test(t : Creature) : boolean + } + class MovementSelector { + - m : Movement + + MovementSelector(m : Movement) + + test(t : Creature) : boolean + } +} +SizeSelector --> "-s" Size +AbstractCreature --> "-color" Color +MovementSelector --> "-m" Movement +AbstractCreature --> "-movement" Movement +AbstractCreature --> "-size" Size +ColorSelector --> "-c" Color +Goblin --|> AbstractCreature +Troll --|> AbstractCreature +AbstractCreature ..|> Creature +Shark --|> AbstractCreature +KillerBee --|> AbstractCreature +Octopus --|> AbstractCreature +Dragon --|> AbstractCreature +@enduml \ No newline at end of file diff --git a/state/etc/state.urm.puml b/state/etc/state.urm.puml new file mode 100644 index 000000000000..a0951ff6e4ef --- /dev/null +++ b/state/etc/state.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.state { + class AngryState { + - mammoth : Mammoth + + AngryState(mammoth : Mammoth) + + observe() + + onEnterState() + } + class Mammoth { + - state : State + + Mammoth() + - changeStateTo(newState : State) + + observe() + + timePasses() + + toString() : String + } + interface State { + + observe() {abstract} + + onEnterState() {abstract} + } + class PeacefulState { + - mammoth : Mammoth + + PeacefulState(mammoth : Mammoth) + + observe() + + onEnterState() + } + class App { + + App() + + main(args : String[]) {static} + } +} +PeacefulState --> "-mammoth" Mammoth +AngryState --> "-mammoth" Mammoth +Mammoth --> "-state" State +AngryState ..|> State +PeacefulState ..|> State +@enduml \ No newline at end of file diff --git a/step-builder/etc/step-builder.urm.puml b/step-builder/etc/step-builder.urm.puml new file mode 100644 index 000000000000..cc1f88ef00ad --- /dev/null +++ b/step-builder/etc/step-builder.urm.puml @@ -0,0 +1,91 @@ +@startuml +package com.iluwatar.stepbuilder { + interface BuildStep { + + build() : Character {abstract} + } + -class CharacterSteps { + - abilities : List + - fighterClass : String + - name : String + - spell : String + - weapon : String + - wizardClass : String + - CharacterSteps() + + build() : Character + + fighterClass(fighterClass : String) : WeaponStep + + name(name : String) : ClassStep + + noAbilities() : BuildStep + + noMoreAbilities() : BuildStep + + noSpell() : BuildStep + + noWeapon() : BuildStep + + withAbility(ability : String) : AbilityStep + + withSpell(spell : String) : AbilityStep + + withWeapon(weapon : String) : AbilityStep + + wizardClass(wizardClass : String) : SpellStep + } + class App { + + App() + + main(args : String[]) {static} + } + interface ClassStep { + + fighterClass(String) : WeaponStep {abstract} + + wizardClass(String) : SpellStep {abstract} + } + interface WeaponStep { + + noWeapon() : BuildStep {abstract} + + withWeapon(String) : AbilityStep {abstract} + } + interface AbilityStep { + + noAbilities() : BuildStep {abstract} + + noMoreAbilities() : BuildStep {abstract} + + withAbility(String) : AbilityStep {abstract} + } + interface NameStep { + + name(String) : ClassStep {abstract} + } + class CharacterStepBuilder { + - CharacterStepBuilder() + + newBuilder() : NameStep {static} + } + class Character { + - abilities : List + - fighterClass : String + - name : String + - spell : String + - weapon : String + - wizardClass : String + + Character(name : String) + + getAbilities() : List + + getFighterClass() : String + + getName() : String + + getSpell() : String + + getWeapon() : String + + getWizardClass() : String + + setAbilities(abilities : List) + + setFighterClass(fighterClass : String) + + setName(name : String) + + setSpell(spell : String) + + setWeapon(weapon : String) + + setWizardClass(wizardClass : String) + + toString() : String + } + interface SpellStep { + + noSpell() : BuildStep {abstract} + + withSpell(String) : AbilityStep {abstract} + } +} +App --+ CharacterStepBuilder +WeaponStep ..+ CharacterStepBuilder +SpellStep ..+ CharacterStepBuilder +AbilityStep ..+ CharacterStepBuilder +ClassStep ..+ CharacterStepBuilder +CharacterSteps ..+ CharacterStepBuilder +NameStep ..+ CharacterStepBuilder +BuildStep ..+ CharacterStepBuilder +CharacterSteps ..|> NameStep +CharacterSteps ..|> ClassStep +CharacterSteps ..|> WeaponStep +CharacterSteps ..|> SpellStep +CharacterSteps ..|> AbilityStep +CharacterSteps ..|> BuildStep +@enduml \ No newline at end of file diff --git a/strategy/etc/strategy.urm.puml b/strategy/etc/strategy.urm.puml new file mode 100644 index 000000000000..2cc07286360b --- /dev/null +++ b/strategy/etc/strategy.urm.puml @@ -0,0 +1,33 @@ +@startuml +package com.iluwatar.strategy { + class DragonSlayer { + - strategy : DragonSlayingStrategy + + DragonSlayer(strategy : DragonSlayingStrategy) + + changeStrategy(strategy : DragonSlayingStrategy) + + goToBattle() + } + class SpellStrategy { + + SpellStrategy() + + execute() + } + class ProjectileStrategy { + + ProjectileStrategy() + + execute() + } + interface DragonSlayingStrategy { + + execute() {abstract} + } + class MeleeStrategy { + + MeleeStrategy() + + execute() + } + class App { + + App() + + main(args : String[]) {static} + } +} +DragonSlayer --> "-strategy" DragonSlayingStrategy +SpellStrategy ..|> DragonSlayingStrategy +ProjectileStrategy ..|> DragonSlayingStrategy +MeleeStrategy ..|> DragonSlayingStrategy +@enduml \ No newline at end of file diff --git a/template-method/etc/template-method.urm.puml b/template-method/etc/template-method.urm.puml new file mode 100644 index 000000000000..c98287bc6ee8 --- /dev/null +++ b/template-method/etc/template-method.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.templatemethod { + class SubtleMethod { + + SubtleMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } + class HitAndRunMethod { + + HitAndRunMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } + abstract class StealingMethod { + + StealingMethod() + # confuseTarget(String) {abstract} + # pickTarget() : String {abstract} + + steal() + # stealTheItem(String) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class HalflingThief { + - method : StealingMethod + + HalflingThief(method : StealingMethod) + + changeMethod(method : StealingMethod) + + steal() + } +} +HalflingThief --> "-method" StealingMethod +SubtleMethod --|> StealingMethod +HitAndRunMethod --|> StealingMethod +@enduml \ No newline at end of file diff --git a/thread-pool/etc/thread-pool.urm.puml b/thread-pool/etc/thread-pool.urm.puml new file mode 100644 index 000000000000..2b73e2d533fd --- /dev/null +++ b/thread-pool/etc/thread-pool.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.threadpool { + class Worker { + - task : Task + + Worker(task : Task) + + run() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Task { + - ID_GENERATOR : AtomicInteger {static} + - id : int + - timeMs : int + + Task(timeMs : int) + + getId() : int + + getTimeMs() : int + + toString() : String + } + class PotatoPeelingTask { + - TIME_PER_POTATO : int {static} + + PotatoPeelingTask(numPotatoes : int) + + toString() : String + } + class CoffeeMakingTask { + - TIME_PER_CUP : int {static} + + CoffeeMakingTask(numCups : int) + + toString() : String + } +} +Worker --> "-task" Task +PotatoPeelingTask --|> Task +CoffeeMakingTask --|> Task +@enduml \ No newline at end of file diff --git a/tolerant-reader/etc/tolerant-reader.urm.puml b/tolerant-reader/etc/tolerant-reader.urm.puml new file mode 100644 index 000000000000..9e2bc83b31b7 --- /dev/null +++ b/tolerant-reader/etc/tolerant-reader.urm.puml @@ -0,0 +1,38 @@ +@startuml +package com.iluwatar.tolerantreader { + class RainbowFishSerializer { + - RainbowFishSerializer() + + readV1(filename : String) : RainbowFish {static} + + writeV1(rainbowFish : RainbowFish, filename : String) {static} + + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} + } + class RainbowFish { + - age : int + - lengthMeters : int + - name : String + - serialVersionUID : long {static} + - weightTons : int + + RainbowFish(name : String, age : int, lengthMeters : int, weightTons : int) + + getAge() : int + + getLengthMeters() : int + + getName() : String + + getWeightTons() : int + } + class RainbowFishV2 { + - angry : boolean + - hungry : boolean + - serialVersionUID : long {static} + - sleeping : boolean + + RainbowFishV2(name : String, age : int, lengthMeters : int, weightTons : int) + + RainbowFishV2(name : String, age : int, lengthMeters : int, weightTons : int, sleeping : boolean, hungry : boolean, angry : boolean) + + getAngry() : boolean + + getHungry() : boolean + + getSleeping() : boolean + } + class App { + + App() + + main(args : String[]) {static} + } +} +RainbowFishV2 --|> RainbowFish +@enduml \ No newline at end of file diff --git a/twin/etc/twin.urm.puml b/twin/etc/twin.urm.puml new file mode 100644 index 000000000000..b95325abb35f --- /dev/null +++ b/twin/etc/twin.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.twin { + class App { + + App() + + main(args : String[]) {static} + - waiting() {static} + } + class BallItem { + - isSuspended : boolean + - twin : BallThread + + BallItem() + + click() + + doDraw() + + move() + + setTwin(twin : BallThread) + } + abstract class GameItem { + + GameItem() + + click() {abstract} + + doDraw() {abstract} + + draw() + } +} +BallItem --|> GameItem +@enduml \ No newline at end of file diff --git a/value-object/etc/value-object.urm.puml b/value-object/etc/value-object.urm.puml new file mode 100644 index 000000000000..223f9195775c --- /dev/null +++ b/value-object/etc/value-object.urm.puml @@ -0,0 +1,21 @@ +@startuml +package com.iluwatar.value.object { + class App { + + App() + + main(args : String[]) {static} + } + class HeroStat { + - intelligence : int + - luck : int + - strength : int + - HeroStat(strength : int, intelligence : int, luck : int) + + equals(obj : Object) : boolean + + getIntelligence() : int + + getLuck() : int + + getStrength() : int + + hashCode() : int + + toString() : String + + valueOf(strength : int, intelligence : int, luck : int) : HeroStat {static} + } +} +@enduml \ No newline at end of file diff --git a/visitor/etc/visitor.urm.puml b/visitor/etc/visitor.urm.puml new file mode 100644 index 000000000000..3f5689f71fc9 --- /dev/null +++ b/visitor/etc/visitor.urm.puml @@ -0,0 +1,57 @@ +@startuml +package com.iluwatar.visitor { + class CommanderVisitor { + + CommanderVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + class Sergeant { + + Sergeant(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class Commander { + + Commander(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + abstract class Unit { + - children : Unit[] + + Unit(children : Unit[]) + + accept(visitor : UnitVisitor) + } + class Soldier { + + Soldier(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class SergeantVisitor { + + SergeantVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + interface UnitVisitor { + + visitCommander(Commander) {abstract} + + visitSergeant(Sergeant) {abstract} + + visitSoldier(Soldier) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class SoldierVisitor { + + SoldierVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } +} +CommanderVisitor ..|> UnitVisitor +Sergeant --|> Unit +Commander --|> Unit +Soldier --|> Unit +SergeantVisitor ..|> UnitVisitor +SoldierVisitor ..|> UnitVisitor +@enduml \ No newline at end of file From 09037b0251cc949d13ce394462969dadb5655ce3 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Tue, 30 Aug 2016 14:06:14 +0200 Subject: [PATCH 146/207] Rename index.md to README.md to conform to our standards, every other file is named README.md --- data-mapper/{index.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data-mapper/{index.md => README.md} (100%) diff --git a/data-mapper/index.md b/data-mapper/README.md similarity index 100% rename from data-mapper/index.md rename to data-mapper/README.md From 9dd1503e6f02d6cf48e549aa97b8273cadd74274 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 15:07:49 +0200 Subject: [PATCH 147/207] Work on #190: add postPumlsToServer.py python script Used to initially post all pumls to the plantuml hosting while preserving the pumlId's --- _scripts/postPumlsToServer.firstrun.output | 190 +++++++++++++++++++++ _scripts/postPumlsToServer.py | 44 +++++ 2 files changed, 234 insertions(+) create mode 100644 _scripts/postPumlsToServer.firstrun.output create mode 100644 _scripts/postPumlsToServer.py diff --git a/_scripts/postPumlsToServer.firstrun.output b/_scripts/postPumlsToServer.firstrun.output new file mode 100644 index 000000000000..ea77a49a51f8 --- /dev/null +++ b/_scripts/postPumlsToServer.firstrun.output @@ -0,0 +1,190 @@ +parent: half-sync-half-async; artifact: half-sync-half-async +Puml Server ID: RScv3SCm3030LU819FRPXg5fIm552tnYPFiyjRi3RkbAaYkdoQr5JBy369vrxz7oaSv6XmPhL3e6TCaJ0msU-CAoilTToyG8DdKOw5z0GzcAlvNAN_WZSD1brBHHPmxv0000 +parent: abstract-document; artifact: abstract-document +Puml Server ID: PSjB3eCm34NHhPG599vtDyQn85L-ifzX-p3lxEf8Twj3MXGDQvyJMFubChxpKN767gucSq07iinEjSNDOACVNvoAUZr6MWoe3QVE_WRnxZ0Mf38b-hkIGlurX_MyehS7 +parent: tolerant-reader; artifact: tolerant-reader +Puml Server ID: NSZ14SCm20NHLf829ExfXaYChGn26lZ4xSVdtFRjSrZJx9AkZnFOyI9olkenSEOxGxmjWnXgMvE6viLWfmz_kNI9SLZP38XRqEIuWx1Kd0t5XVjjGVj_DNtMdLD_ +parent: event-driven-architecture; artifact: event-driven-architecture +Puml Server ID: TOhH3SCW30LNQGS0_tSRnrZ15H1adfFromBzkfFktZQaHT7mzgh0N1yYvoUVXXf7B7Mv1dGWozN9MZmCTlhopQdeidEaoO3wMDHvRI6zzvwAssPYbsfGGRYIGlxN7DxpZDv- +parent: publish-subscribe; artifact: publish-subscribe +Puml Server ID: PSZB3SCm203GLTe1RExT1XCKKs5YyMdMR--zFRsd66aTNAwFcRdZ1U1uzrDorgXWfykIBJjT2qJhnaI7Dtwm7HnoMjkOoMu12-C7s3LKOhQe4UGo63ZfVtlvwhkMVW40 +parent: facade; artifact: facade +Puml Server ID: BSP15eCm20N0gxG7CEoz3ILKqvTW7dpq-hhehERTJ7fMJU-l7PYn4ZbVPMlOyvEXBeT13KMEGQtdnM2d7v-yL8sssJ8PKBUWmV64lYnSbHJoRqaVPUReDm00 +parent: service-locator; artifact: service-locator +Puml Server ID: NSjB3iCm203HgxG7iDdtDeIWX0fZYqzo_MRTtUX9ynOZhPtBzNLchlW0EDxza3nhgs2dQScMdUO0qRenqU6B5xQTGmvh2pFPBM1WF07FSmbnqqcOqu6J_gsNZxvgw0y0 +parent: dao; artifact: dao +Puml Server ID: 5SR14OKW30N0LhG0oVrt4o6ZE12Ov4NR_thQNQlc5aN2sd82qtz4naywAixOmyNoK8WYvT6fjdWOR7JnpLiHhuTkam4nTUhiRwZm847-J64zpUZj3m00 +parent: model-view-presenter; artifact: model-view-presenter +Puml Server ID: ROlR3SGW3C1MkGu0-RzjKeXQJWcWFChwPO3xispvQBrmL0hbp-q-xGkWkFBL_8upZBICxjGzbo7GE1OwAlpmmLJ9sjNJH7VIRY1e6q169KvFevMcakrtI_BoD-HGoJE4Nm00 +parent: observer; artifact: observer +Puml Server ID: FSkn4OGm30NHLg00hFow4KO3PcpP8tr1-pYwx6smQz5Suv2mkbp0y1-HyPlEWYlsSB7S5Q98kJSgDLu66ztyy7Q8brEtmO2OEZNs2Uhxl9u9GVv72cjfHAiV +parent: intercepting-filter; artifact: intercepting-filter +Puml Server ID: RSfB3i8m303Hgy014k-vZN5DQkIuaJ_q-fGzkz7JtCL8Q-DolUsPAnu0ZcSVadizAzZfi6JBJiS4qJenqU6D7smRXmnh2pFPBM1YN05o_KwyKcoqb-ZFEEcVz_BPLqtz0W00 +parent: factory-method; artifact: factory-method +Puml Server ID: NSZB3G8n30N0Lg20n7UwCOxPP9MVx6TMT0zdRgEvjoazYeRrMmMsFuYChtmqr7Y6gycQq8aiQr3hSJ7OwEGtfwBUZfas0shJQR3_G2yMBFkaeQYha4B-AeUDl6FqBm00 +parent: private-class-data; artifact: private-class-data +Puml Server ID: RShR3SCm243HLTe1RFwx3S4eeSB4uf6itmpGlwkZ-nOZhS7b-ZeoLtm07E--InwrLR3JQScMdSu9edLZeiCNBso3GtPh2pFPBM1YF07BvSBaHeeHRJm_SD8VxkMphvhw0m00 +parent: async-method-invocation; artifact: async-method-invocation +Puml Server ID: TSdB3SCW303GLTe1mFTkunWhk0A3_4dKxTi5UdlIUuhIoCPfuz4Zjhy03EzwIlGyqjbeQR16fJL1HjuOQF362qjZbrFBnWWsTPZeFm3wHwbCZhvQ4RqMOSXIuA1_LzDctJd75m00 +parent: execute-around; artifact: execute-around +Puml Server ID: NSZ14G8n20NGLhI0XBlT865suoGa0n_NylNixSsxTvEHJTF7xGHsF8YShtfqdFdCK9TbK4ELDQcFl1ZizE8tbwRH3okR0NKBcXm_a7vK4bhOLreZXVnLJPzrvnnV +parent: monostate; artifact: monostate +Puml Server ID: HSV14OGm20NGLjO23FVj1YEZsGaa0nzjVxrvUszfLdlkaju_9p3ZI-HybwFXp2r3l0w364eTIgtdpM2d7r-yxXBji7Ko86v1ol60TDW8C8G4zLr9rp9J-ny0 +parent: thread-pool; artifact: thread-pool +Puml Server ID: JSV14SCW30J0Lk82GFzq8uF6a1624IUx_UIPt-xHhMXK2TTN0zP-4pa_-UfeSSOMBzCWXbpceAxnCDZfmpUdAhjVbXO3uhPfyFw1q5oufZMdag3yFuUFl6Be5m00 +parent: delegation; artifact: delegation +Puml Server ID: JSV14GCX20NGLf82LkxfXbN69OFeu2VRVdBCxRsdUhLiac6F2rZxHHHybwwuyimjKQT37ANEGMfvCpZepHy-ccpjVYm697pJuFq3DJ7f39rEWlhNaZ7Aoc5V +parent: chain; artifact: chain +Puml Server ID: 9SR13SCm20NGLTe1OkxTXX0KKzd4Wa-pVYlrdTxJN4OTMZ4U7LZv8Wg-ssdejLTgoELGHvDhaesw6HpqvWzlXwQTlYq6D3nfSlv2qjcS5F9VgvXjrHnV +parent: resource-acquisition-is-initialization; artifact: resource-acquisition-is-initialization +Puml Server ID: ZShR3S8m343HLUW0YV_PnhXMQvGumOzMOdhA1lqxkhgBABLSEQqzzeZfJm33isuIUxxIsMXei4QbqK5QdXXeyCO3oyekcvQ94MpgqD4lWB6FDEA2z4bn2HbQn8leHMponNy13hgvrhHUP_Rs0m00 +parent: fluentinterface; artifact: fluentinterface +Puml Server ID: NOj93eCm302_KXv0VEzlN6F0bMCYB_3zvjpRQ3IpY97MnkNwEZD7l04SdtP8dlMfOAVBaYqRNHr4wy54Xo_Uk6uSSjWwC9FT0Zh61DYrPY_pyXs9WPF-NIllRLJN7m00 +parent: service-layer; artifact: service-layer +Puml Server ID: LOl93SCm3C1MQGUmzUysgY8aAcJ5q96WszVV_aW2V8gHriRb-ZWoPxm07E--Inxrhc2dqv8jEvq3HEl6H8SFNjWs3jcjJSnaju21iG3MSmbnK_mkuwJ_qij7dpNq1m00 +parent: visitor; artifact: visitor +Puml Server ID: DSR14OGm20NGLhG0mtsxmSWeJa8oyD7sTo_xJczLgoqFIM_B1Spu43c_vLHSkMU8rs4GGwcZaxPy6UnqyyFR8Q6dRPC1SGlg7B_Gew4OJeBwVqdlPMPlNm00 +parent: double-dispatch; artifact: double-dispatch +Puml Server ID: NSbB3iCW303HgpG70Ezx6yTOWSeOv4zp_MRTtUZDCPGa6wV9gqTiVmCOtlKQqVDCPwEbmHgLreGXUMEWmGU_M1hxkBHiZ61JXud-1BILft1fmvz37JZetshQh3kd_000 +parent: monad; artifact: monad +Puml Server ID: 9SR13SCm20NGLPe1OkxTXjWeSMMm1Pza_LRgExsjMntP97syBc35cyZvAMV7bKU6U9q6CPGwbVh8Xy5E7xvvRnBzj7qn86v1ol4BwJHk9AZ_bNGjAtLy0G00 +parent: front-controller; artifact: front-controller +Puml Server ID: PSlB3OGm303HLfO24j-t6nCC13bEvC_IFk6yjz6JPgbIE3OAvS_fFkmBe7Zde_ePQnXfwU8adajlK3bkT5Iuy8Tf8wk7f87kf6BGq6R0hlD8xwQTUG9v-SCSslA8nWy0 +parent: strategy; artifact: strategy +Puml Server ID: FSV13OCm30NGLM00udktCS4AGOaJsTz5tRwSkBstLiqj3WbhombC_n0PtwbKdB67Y-MX44NAerDjSJFOwE8lRuTuBRfD1iJKgRC_88SnfFn8aD-ai9vczFO7 +parent: command; artifact: command +Puml Server ID: DSgn4OCm30NGLM00h3xR25i7vYpXaxx2-g59zugtTgiZcwIFvGHcV8YSdt9qdBbdYDVR88PIRwK-yc6mqyLVtff4FsoR38XRa7Aye3SgMoD1_RkaQvcfumS0 +parent: abstract-factory; artifact: abstract-factory +Puml Server ID: PSZB3OD034NHLa81Czwd6sCC39gVxEUWT1_ssLmTtQLqgR5fM7sTmFGtaV6TZu8prd0r6HtQaMKqAZLk1XjT_E6qgPUZfyc0MdTgx0-8LuUn8ErFXdr98NypXxKyezKV +parent: flux; artifact: flux +Puml Server ID: 7SP14eCm20NGg-W13FlU1YFLE0GpyAazVZk-rPkRLSrDqdKwW14l8kUxx0r7hXdYzJA8eTIhKzEy6UnqyeUNJQBjjWm6n2seS_n3Ryql2UgJajxBoAu_ +parent: event-aggregator; artifact: event-aggregator +Puml Server ID: PSf13iCW30NHgxG70Ezx6uTOX0eCih-JwvTzTwEdUJSjFKu9wwyBMFuXCdvoRRZY21ShKo6ANEQWrkDXiD6NRqwdUAkQ5WDYwZJOTv3SUqzSgqbbp0qeVvZ3Hbun-Wy0 +parent: singleton; artifact: singleton +Puml Server ID: HSV14SCm20J0Lk82BFxf1ikCh0n26ZZizfDVVhjRjwfvIhg-Bc35cyZvAQtZoYD3l4w364gTWxhcms2d3z-ydnAzsRuO4BUWmV43HRUcWcaagF-Lz55M3lq2 +parent: null-object; artifact: null-object +Puml Server ID: JSV14SCm20J0Lk829Fxf1cF6bWSX3JhYzfDdVhjRSx4yDCDU5p3NcoZugMV3bNik3HaETLGPdPhbm-2WcpzS3btjz38PqF15dTSFv6bMndwhW1Jo_vhHwynkNm00 +parent: multiton; artifact: multiton +Puml Server ID: FST14i8m20NGg-W16lRUXgPCYnD81Zxs-hfozzvJlOywf68yBc3bYoZuRgVYghrIea-7E5gVHZhgPd3Gcp-y7P9w-hOOaF0au_o1h0OKqqdG_saLrbRP-080 +parent: composite; artifact: composite +Puml Server ID: HSf13eCm30NHgy01YFUzZGaM62LEP7-NwvTTT_EaMTLgoqFIst81Cpv4payv5LVk6U9r6CHGwkYaBHy6EztyvUsGqDEsoO2u1NMED-WTvmY5aA3-LT9xcTdR3m00 +parent: api-gateway; artifact: image-microservice +Puml Server ID: 3Sp13SCm2030LTe1RFxTXX3aK1biOOZLxPlVlUujHZrFJk-lAsAk3u3ZhatYoYCNEmqBjgWq5AJdna27BzvOJbxIh4oCOBS5Yki1u9JIC7ZZ3pW8HB5nKI4VJtSBSKtNEbFx7m00 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'api-gateway/etc/image-microservice.urm.puml' +pumlid: 3Sp13SCm2030LTe1RFxTXX3aK1biOOZLxPlVlUujHZrFJk-lAsAk3u3ZhatYoYCNEmqBjgWq5AJdna27BzvOJbxIh4oCOBS5Yki1u9JIC7ZZ3pW8HB5nKI4VJtSBSKtNEbFx7m00 +parent: api-gateway; artifact: api-gateway-service +Puml Server ID: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'api-gateway/etc/api-gateway-service.urm.puml' +pumlid: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0 +parent: api-gateway; artifact: price-microservice +Puml Server ID: 3Sn13iGW243HgqmFeEpdDfGIoqJK8DJqzkFklyq_f56DYyFgvtOVymjWk78Hl-ECoKQzEJVFr1Mana97Wny-c2wUKbeQwCxM9YZE7O13Ka7dXI-m4mmJugH2rlVksSXXcaTe_GC0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'api-gateway/etc/price-microservice.urm.puml' +pumlid: 3Sn13iGW243HgqmFeEpdDfGIoqJK8DJqzkFklyq_f56DYyFgvtOVymjWk78Hl-ECoKQzEJVFr1Mana97Wny-c2wUKbeQwCxM9YZE7O13Ka7dXI-m4mmJugH2rlVksSXXcaTe_GC0 +parent: object-pool; artifact: object-pool +Puml Server ID: JSV94SCm2030Lk829Fxf1cF6bWU1XYDkFtdcjxiD9Qc3o-LrPQvu0pW-_HnvrLx1JgR9cfrimf1wCD7XnW-sWsESsXPcicl0nFW1RB-PiYqp0KxwVo-VVTMKBm00 +parent: adapter; artifact: adapter +Puml Server ID: DSR14S8m30J0Lg20M7-wEMnDOiPMFDA9j0yyUEtUkzMHJTF7xI1NF4GSLzaxZtncgDVJgCPIpobzv0N2vOKtjgRHTziMI7KBcOXl10thfxB-Nz9dMJd71m00 +parent: hexagonal; artifact: hexagonal +Puml Server ID: HSTB4W8X30N0g-W1XkozpPD90LO8L3wEnzUTk-xxq2fvSfhSUiJs1v7XAcr4psSwMrqQh57gcZGaBmICNdZZEDb7qsCZWasT9lm7wln1MmeXZlfVIPjbvvGl +parent: value-object; artifact: value-object +Puml Server ID: LSZ13SCm20NGLTe1RExTXX2KECBOmfza_VRQszDxDnVBNJFiTG9pVOY2dteqdBdbqf3XK4ULqQbPFWmEklZcikjgXvV9W8Olwhn-e9ijjOpjKW4fv2zgHgypktq1 +parent: twin; artifact: twin +Puml Server ID: 7SR13OCm30NGLUW0n7UsCS42eyH4zdUpFbNVwNtKQij3qjjo0ICs8kTPJiMLUuPuVGnYAFNff2qdWvrk_l9wIEXfws10t88wno-4gKQ2-az9xsLaRoy0 +parent: semaphore; artifact: semaphore +Puml Server ID: HSV14SCm20J0Lk82BFxf1ikCfOn06ZZizfDVVhjRjphobFJnQi2ADv7pKwwEbaU6U9q6CPGwbVh8Xy5E7xvvFoNwPVjYGDo2bEC72b5URRgGeFvNqhMirF45 +parent: message-channel; artifact: message-channel +Puml Server ID: NSZB3SCm203GLTe1RExTXX1akm9YyMdMRy-zFRtdCf8wkLmUCtF72y3nxcFbhAE2dIvBjknqAIof6nCTtlZ1TdAiOMrZ9hi5ACOFe1o1WnjDD6C1Jlg_NgvzbyeN +parent: poison-pill; artifact: poison-pill +Puml Server ID: JSZ14SCm20NHLf82BExfXiYCJGOX3NpYzkDZRllsgTwjTgcmnmciV145N-rGdFMkbEZJ8OxMvo2rkXWSzE4lRxka7huj1YGyQN3UGMjgpdkh6Gdwlrl5QAk6_G00 +parent: aggregator-microservices; artifact: aggregator-service +Puml Server ID: JOov3SCm301NIGQGs7iRXYPa1g8ayB7NjuiKwGvtmBrbKC-Tq_hhY5Y-0HXUjKaS-Kbdepc2HrIQ2jBpma23BvvOTdPfeooCO1iEYlu0O6l63MDQKI6Rp-CKOWSE-ey_NzEqhjH-0m00 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'aggregator-microservices/etc/aggregator-service.urm.puml' +pumlid: JOov3SCm301NIGQGs7iRXYPa1g8ayB7NjuiKwGvtmBrbKC-Tq_hhY5Y-0HXUjKaS-Kbdepc2HrIQ2jBpma23BvvOTdPfeooCO1iEYlu0O6l63MDQKI6Rp-CKOWSE-ey_NzEqhjH-0m00 +parent: aggregator-microservices; artifact: information-microservice +Puml Server ID: LSnB3i8m303Hgy016k-vZN5DQXGxaJ_jzUcMtKXFcgSOZTgvV3oEp1Kl0CUhTScZtXNiD2tPij5Ka54N9ZfyySHjvv1ksy9CTWjGZ3i0UtVkcDCt5V9vFquX3k0a4FjCLqoPzgUjNDig7Jy0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'aggregator-microservices/etc/information-microservice.urm.puml' +pumlid: LSnB3i8m303Hgy016k-vZN5DQXGxaJ_jzUcMtKXFcgSOZTgvV3oEp1Kl0CUhTScZtXNiD2tPij5Ka54N9ZfyySHjvv1ksy9CTWjGZ3i0UtVkcDCt5V9vFquX3k0a4FjCLqoPzgUjNDig7Jy0 +parent: aggregator-microservices; artifact: inventory-microservice +Puml Server ID: LSpB3G8n303HLg20ZUzqOxnMrYXn8d-oedjovJRIIEyfIYrFJckFAsBw2y3mBbNYodSw6mqDrYWqEaZB6mCDFhZmEDcbwZ4nWaqTEleEm5gDAyQmemlPsCOIOWSE0j6riM7VlrVIUfdPsmy0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'aggregator-microservices/etc/inventory-microservice.urm.puml' +pumlid: LSpB3G8n303HLg20ZUzqOxnMrYXn8d-oedjovJRIIEyfIYrFJckFAsBw2y3mBbNYodSw6mqDrYWqEaZB6mCDFhZmEDcbwZ4nWaqTEleEm5gDAyQmemlPsCOIOWSE0j6riM7VlrVIUfdPsmy0 +parent: bridge; artifact: bridge +Puml Server ID: BSR14SCm20J0Lf82BFxf1akCJ4R26ZZYzkE7zxLljJgoIVfu7S2A3v7pLRhYo3r3l9u6CPHwJjAH5uETllpZhKbejsqn86v1a-CExQwj2mdgqv8-oyev_W00 +parent: servant; artifact: servant +Puml Server ID: DSkn4O0m20NGLNG0G-ys63cDbv0SV7HzRUnUy-QYkSOkONKwWU4haV6JZe8pjd2nt1MYIBatAZKU1XjTVFEoYvT3by60c3erzW_qdPiL9CY_KrXB8rfz0G00 +parent: lazy-loading; artifact: lazy-loading +Puml Server ID: LSXB3W8X303Gg-W1e7jlqu66gIc5zED4JwzRTo_lpjeaEwN9xOpO_W0mlEhWEFD89sjBWpHgMnDOyi90WoU-i7Ho7besHf2fmqJ_0GG_xo8BE-i0YlONDMtMdLE- +parent: flyweight; artifact: flyweight +Puml Server ID: HSV94S8m3030Lg20M7-w4OvYAoCh7Xtnq3ty-Eq-MQlaJcdow17JNm26gpIEdkzqidffa4Qfrm2MN1XeSEADsqxEJRU94MJgCD1_W4C-YxZr08hwNqaRPUQGBm00 +parent: mutex; artifact: mutex +Puml Server ID: 9SR13OCm30NGLSe0n7UsCS62LB69x6zWV2hrdTxKhFRS9Br_3c34GkHybxtXo3L3l9u6CPHwAhMUDuETldpnl4cqtUR1WBW5ASSlf0bvI53_A-bQHcf_0G00 +parent: mediator; artifact: mediator +Puml Server ID: FSV14SCm20J0Lk82BFxf1akCJKOW3JhizfDNVhkRUktP9AE_Bc2kDr7mKqx5bKSkYJeSuYXr66dFXy517xvvRxBqz7qo8E6BZDSFPDAKCO84zP-IOMMczIy0 +parent: page-object; artifact: page-object +Puml Server ID: JSV14OGW30NGLjO28FVj9iOCua1Wme-sxnxtzjvMJLeS6ju-9p3NbyZvoQNYZ3sMkWo36hACJhN5ms2dYszEXwvQB4q6r6rHv_K3JIwQndwfW1Jo_npUyupUNW00 +parent: factory-kit; artifact: factory-kit +Puml Server ID: JST15i8m20N0g-W14lRU1YcsQ4BooCS-RwzBTpDNSscvQKQx7C1SDwBWi-w68--vD6Gur55bTBAM9uE3dlpcikcotSjaGCCNTLu_q8C58pxbPI25_Bzcz3gpjoy0 +parent: property; artifact: property +Puml Server ID: FSV13OCm30NGLTe1YEziumOBKYMEPN-3s9wUUdlltRJst2Izlmx0OYLolihUSEGdGxnEXIXAdODQpul1Jby-UTaasgwBCI2kGOFZ1pAV9ewR1FMVaZwAvUWF +parent: dependency-injection; artifact: dependency-injection +Puml Server ID: RSdB3SCW303GLPe1mFTkunWhSGG6-PEesxS3zFQajubIpyPf_NL6B7y363xra3XpJsUZgS4QbUO0wVbWeC65DvR6BeUMXH5iwZ3GVu36YxMnqgU8NamXKu63_aPD6tNbw5y0 +parent: layers; artifact: layers +Puml Server ID: BSR13OCm30NGLSe0n7UsCS62L8w9x6yGszD3t-bDpQhc9kdwEO0H2v7pNVQ68zSCyNeQn53gsQbftWns-lB5yoRHTfi70-8Mr3b-8UL7F4XG_otflOpi-W80 +parent: producer-consumer; artifact: producer-consumer +Puml Server ID: PSjB3iCW303HgxG70Ezx6zTO2HKso9_a-c7VtUX9y-vA8nkdZTSPiVm3O7ZNeyUPttGscXgiKMaAz94t1XhyyCBIsFkXPM44cpe8-WvODbiIMzcdfspXe7-jQL9NodW0 +parent: builder; artifact: builder +Puml Server ID: DSR94O0m2030LhG0mzzkC64KXs26GzlNZw_TcRLADagJwOWOlW8OFcNdE79B9wkN1ccKUdLWoGS33KwySMdalEioC89C7Jhw5zYIfNrIrFybhPUHNLu0 +parent: specification; artifact: specification +Puml Server ID: LSX14i8m20NGg-W16lRU1YcsE0d9mCTUNxVkthoxkVJQjQBVJc3bWoZuQeVXh6UbXao7EfhCGTRhOd3Gcp-yxPfs-BOOqF2amVa3vLAnbmd3ffD2_gTLZBPgz2y0 +parent: state; artifact: state +Puml Server ID: 9SRH3O0m20N0LNG0ox_RO2LQqz867hg-9jxNpKLpZLt2wdG2mrSYuoST1MTiuMAvAqIHSczKQZmCDhhuvcKNBuSkWm4nTMhiNyZ141BaVocifH6jlW00 +parent: reader-writer-lock; artifact: reader-writer-lock +Puml Server ID: RSZB4S8m303HLg00MtUw4R8cCP5bZpwuVL80jttxx4gIZTFaSKOiVm4OxdhqEFETpaPJWpKgpG5TScEWmGU_M1fxFxGiZ61JXu5-1nXZOolR-gqYaoxWe3-xfeswSiWF +parent: interpreter; artifact: interpreter +Puml Server ID: JSf13eCm30NHgz034E-vZGaM62Kcih_BzQ6xxjv8yr6hBJT9RzC1Z5Y8dE-oAuvSCyJhPH13gLSdRNapsEdaBy-RXEus3mR4BQXpl21zVnykFmlgVvVqNaRszW00 +parent: template-method; artifact: template-method +Puml Server ID: NSZ13SCW30NGLPe1mFTkuu0Lg6n0vZjPlpttzlIEFef6bN1zDM3jDv7paw-E5cTiyJ87P22NQTGr7WOxVVZcL6NtQwJ5WFZOPBn_88WjPKWoGPkL1EN_ShZb5QPV +parent: feature-toggle; artifact: feature-toggle +Puml Server ID: NSZ14G8X30NGLhG0oDrk8XjPd12OvCTjNy_UthpxiAPvIBhUJc37WyZvgdtWp6U6U5i6CTIs9WtDYy5ER_vmEIH6jx8P4BUWoV43lOIHBWMhTnKIjB-gwRFkdFe5 +parent: business-delegate; artifact: business-delegate +Puml Server ID: POl13SCm3CHMQGU8zUysgYCuBcJ5a4x9-l6_Fu84tzsgvYxf-Zg06HyYvxkqZYE_6UBrD8YXr7DGrxmPxFJZYxTTeZVR9WFY5ZGu5j2wkad4wYgD8IIe_xQaZp9pw0C0 +parent: naked-objects; artifact: naked-objects-integtests +Puml Server ID: LSmn4iCW30NHgoG70FMvZGmQ6ni48tt5ru_RT3kls7VJqgDAM7yTmF8FaV6TzuOZjd2nCXMYo6KEQZrk1XkT_ELKnTkkQJ4Wfaw3_GbIlgIckPrIu2Ge_vBQyziX3izX8wyO_GS0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'naked-objects/etc/naked-objects-integtests.urm.puml' +pumlid: LSmn4iCW30NHgoG70FMvZGmQ6ni48tt5ru_RT3kls7VJqgDAM7yTmF8FaV6TzuOZjd2nCXMYo6KEQZrk1XkT_ELKnTkkQJ4Wfaw3_GbIlgIckPrIu2Ge_vBQyziX3izX8wyO_GS0 +parent: naked-objects; artifact: naked-objects-dom +Puml Server ID: LSZ94SCW3030Lf82G7zt8mkDZOC4eyDkF_dcjxFlhZIoSTfudH7BDm33fnuzpjpJsMXgi4QbAT17FXXeSE6DfR7tGyl223Pr4FGVGF73hSpzOWe73lgVqgRKDAahPNm1 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'naked-objects/etc/naked-objects-dom.urm.puml' +pumlid: LSZ94SCW3030Lf82G7zt8mkDZOC4eyDkF_dcjxFlhZIoSTfudH7BDm33fnuzpjpJsMXgi4QbAT17FXXeSE6DfR7tGyl223Pr4FGVGF73hSpzOWe73lgVqgRKDAahPNm1 +parent: naked-objects; artifact: naked-objects-fixture +Puml Server ID: LSX15i8W30N0g-W187jlaq9igH1uoO_r-BfrDs_kJKkFAc7zTW3B7qJ6LzuRZjZ2nSfKY2ANEQZrk1XiTFARKnLlkwR5W9Ww3VOVIFabDStjb08dGVcVz6mVX4aE6td5w5y0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'naked-objects/etc/naked-objects-fixture.urm.puml' +pumlid: LSX15i8W30N0g-W187jlaq9igH1uoO_r-BfrDs_kJKkFAc7zTW3B7qJ6LzuRZjZ2nSfKY2ANEQZrk1XiTFARKnLlkwR5W9Ww3VOVIFabDStjb08dGVcVz6mVX4aE6td5w5y0 +parent: model-view-controller; artifact: model-view-controller +Puml Server ID: ROl13SCm201NQGUm-NSRQgE42h258Lw_wR-_qvtkoTOaEwNBuuoOwmNWkEl1SUOx5taR5cHHsr1WoOs13X-yi7HQV5YP645k2nJN3Q2ZavIBQPVVwqFajXJjVwdfMcUgV040 +parent: proxy; artifact: proxy +Puml Server ID: 9SR13OCm30NGLM00udktCS62eCI9x6yesrEfx_Jcehd69c5rEe3X7oBZE-q5HwpXOhahH95oRrHgt0msEldYPHClkow30J5rQko_qB3-VKYG_qjXBOrezGK0 +parent: memento; artifact: memento +Puml Server ID: DSgn4OCm30NGLM00h3xR2AC3SvRiaxx2-g59zugtDgiz3qdlomNC-10vF-Lik7BF4A_388PIXrBh-J3OwUOlRuT4EssR38XRa7Ay81Lz_o11_RkaQvcf_GS0 +parent: decorator; artifact: decorator +Puml Server ID: HSV14SCm20J0Lk82BFxf1YF6LaP26ZZizfDVVhjRC-bPDRs_Bc35cyZvAMV3bKU6kao36ehCGQtdms2d3z-yLursshuOKBUWmV43LPNfZEcaaFzA-YWhH_y2 +parent: data-mapper; artifact: data-mapper +Puml Server ID: JShB3OGm303HLg20nFVjnYGM1CN6ycTfVtFSsnjfzY5jPgUqkLqHwXy0mxUU8wuyqidQ8q4IjJqCO-QBWGOtVh5qyd5AKOmW4mT6Nu2-ZiAekapH_hkcSTNa-GC0 +parent: caching; artifact: caching +Puml Server ID: DSRB4OKm2030LhG0m_rrWyWaE0bc-6ZxpujxsbMKUXwSrfSMCVq7OFYKAj5oJsUZIuCr2bq3fEU3WGOdthWTx59rcnZ1fWu3_GqGKXEjm47VIzeeCqV_0m00 +parent: reactor; artifact: reactor +Puml Server ID: DSR14OGm20NGLjO23FVj1f7Hx2Ga0nzjVxtuJc-f9YrtJM-V4vZn9NA-or5nvfQXBiEWXYAZKsrvCzZfnnUlkqOzR9qCg5jGvtX2hYmOJWfvNz9xcTdR7m00 +parent: iterator; artifact: iterator +Puml Server ID: FSV13OGm30NHLg00uljsOu85HeaJsTzB-yjfBwCtgrfjUKXwMovWneV8-IcduiezGxmEWnXA7PsqvSDWfvk_l1qIUjes6H2teCxnWlGDOpW9wdzAUYypU_i1 +parent: callback; artifact: callback +Puml Server ID: FSVB4S8m30N0Lg20M7UwUL4qYOciUFGXxSE9s-wp6sjjKgwF8tF6YyXnjxtdKMk5E5-MOjdu6jIrRYIStlXWsIJwRij4fhW53SGFn51TmIT9yZ-jVBHPGxy0 +parent: repository; artifact: repository +Puml Server ID: JSV13OCm30NGLM00udktCS42eyI9xE-YRjyUUtjlLQij3qblomNCU14vF-LKNBbdYDTX44EfevEsV1ZiTFERjqD2Jzic0-8Mr3b-89SvGZ7yGuBwrvBUoypUlW00 +parent: mute-idiom; artifact: mute-idiom +Puml Server ID: JSf13iCm20NHgxG7iDdtDjH62PKX5luarq-MtSsJvgtUHdR96AyTcEj357pLJR7dDvT4EnpYgEqmqf4NWuD-V7BfidJpCXcGy4N6wmcoX1Jj-lo2ziUQONMcZHi0 +parent: prototype; artifact: prototype +Puml Server ID: HSV13OCm30NGLM00udktCS62eCInxE-YRj_UUdjlRLfx7fBUbmkmU14vF-Lik7BF4AzJ8OfIvw3Mys6mqyrltWw9Tkfc38XhqE3uWSmd9Zuc9AZ_bVHHB4V_0W00 +parent: step-builder; artifact: step-builder +Puml Server ID: LOZ93SCm3C1MQGQmzUysYYqaAcJ5q96i7t_x8KXkh4soKvfypeZfNm33fnuSP-xfPEtI88tQhW4i-M2WmGzlB9sS3oqJ8yZKOQ0lWOLPzcJfAoZQtwXfeyuSyW80 +parent: double-checked-locking; artifact: double-checked-locking +Puml Server ID: TSdH4SCW203GLTe1bFzkGv1J6qGFeLc_MI1_x-wzkv94uJ1vDVUrFm26LwxTMnonsMYgitgcEQ1BNEXeyCKVfiAxLqqBtTbqmy1z0ygCGpXHOpgv99bqTgt0JW-LmqPUCUGF diff --git a/_scripts/postPumlsToServer.py b/_scripts/postPumlsToServer.py new file mode 100644 index 000000000000..2f975d3d870a --- /dev/null +++ b/_scripts/postPumlsToServer.py @@ -0,0 +1,44 @@ +import requests, glob, re, os + +# taken from here: http://stackoverflow.com/a/13641746 +def replace(file, pattern, subst): + # Read contents from file as a single string + file_handle = open(file, 'r') + file_string = file_handle.read() + file_handle.close() + + # Use RE package to allow for replacement (also allowing for (multiline) REGEX) + file_string = (re.sub(pattern, subst, file_string)) + + # Write contents to file. + # Using mode 'w' truncates the file. + file_handle = open(file, 'w') + file_handle.write(file_string) + file_handle.close() + +# list of all puml files +fileList = glob.glob('*/etc/*.puml') +for puml in fileList: + pathSplit = puml.split("/") + # parent folder + parent = pathSplit[0] + # individual artifact/project name + artifact = pathSplit[2].replace(".urm.puml", "") + print "parent: " + parent + "; artifact: " + artifact + + # do a POST to the official plantuml hosting site with a little trick "!includeurl" and raw github content + data = { + 'text': "!includeurl https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/" + puml + } + r = requests.post('http://plantuml.com/plantuml/uml', data=data) + pumlId = r.url.replace("http://plantuml.com/plantuml/uml/", "") + + # the only thing needed to get a png/svg/ascii from the server back + print "Puml Server ID: " + pumlId + + # add the id so jekyll/liquid can use it + if (parent == artifact): + replace("./" + parent + "/README.md", "categories:", "pumlid: {}\\ncategories:".format(pumlId)) + else: + print "I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file '" + puml + "'\npumlid: {}".format(pumlId) + From 58dce1bd89a5b448b238ec3e0fe8adc77b15c41f Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 15:10:34 +0200 Subject: [PATCH 148/207] Work on #190: Commit initial pumlId's added by the script --- abstract-document/README.md | 1 + abstract-factory/README.md | 1 + adapter/README.md | 1 + aggregator-microservices/README.md | 1 + api-gateway/README.md | 1 + async-method-invocation/README.md | 1 + bridge/README.md | 1 + builder/README.md | 1 + business-delegate/README.md | 1 + caching/README.md | 1 + callback/README.md | 1 + chain/README.md | 1 + command/README.md | 1 + composite/README.md | 1 + dao/README.md | 1 + data-mapper/README.md | 1 + decorator/README.md | 1 + delegation/README.md | 1 + dependency-injection/README.md | 1 + double-checked-locking/README.md | 1 + double-dispatch/README.md | 1 + event-aggregator/README.md | 1 + event-driven-architecture/README.md | 1 + execute-around/README.md | 1 + facade/README.md | 1 + factory-kit/README.md | 1 + factory-method/README.md | 1 + feature-toggle/README.md | 1 + fluentinterface/README.md | 1 + flux/README.md | 1 + flyweight/README.md | 1 + front-controller/README.md | 1 + half-sync-half-async/README.md | 1 + hexagonal/README.md | 1 + intercepting-filter/README.md | 1 + interpreter/README.md | 1 + iterator/README.md | 1 + layers/README.md | 1 + lazy-loading/README.md | 1 + mediator/README.md | 1 + memento/README.md | 1 + message-channel/README.md | 1 + model-view-controller/README.md | 1 + model-view-presenter/README.md | 1 + monad/README.md | 1 + monostate/README.md | 1 + multiton/README.md | 1 + mute-idiom/README.md | 1 + mutex/README.md | 1 + naked-objects/README.md | 1 + null-object/README.md | 1 + object-pool/README.md | 1 + observer/README.md | 1 + page-object/README.md | 1 + poison-pill/README.md | 1 + private-class-data/README.md | 1 + producer-consumer/README.md | 1 + property/README.md | 1 + prototype/README.md | 1 + proxy/README.md | 1 + publish-subscribe/README.md | 1 + reactor/README.md | 1 + reader-writer-lock/README.md | 1 + repository/README.md | 1 + resource-acquisition-is-initialization/README.md | 1 + semaphore/README.md | 1 + servant/README.md | 1 + service-layer/README.md | 1 + service-locator/README.md | 1 + singleton/README.md | 1 + specification/README.md | 1 + state/README.md | 1 + step-builder/README.md | 1 + strategy/README.md | 1 + template-method/README.md | 1 + thread-pool/README.md | 1 + tolerant-reader/README.md | 1 + twin/README.md | 1 + value-object/README.md | 1 + visitor/README.md | 1 + 80 files changed, 80 insertions(+) diff --git a/abstract-document/README.md b/abstract-document/README.md index bf28ff999bf5..c8755ce078a0 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -3,6 +3,7 @@ layout: pattern title: Abstract Document folder: abstract-document permalink: /patterns/abstract-document/ +pumlid: PSjB3eCm34NHhPG599vtDyQn85L-ifzX-p3lxEf8Twj3MXGDQvyJMFubChxpKN767gucSq07iinEjSNDOACVNvoAUZr6MWoe3QVE_WRnxZ0Mf38b-hkIGlurX_MyehS7 categories: Structural tags: - Java diff --git a/abstract-factory/README.md b/abstract-factory/README.md index 485599b984fb..2a8fecd3674f 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -3,6 +3,7 @@ layout: pattern title: Abstract Factory folder: abstract-factory permalink: /patterns/abstract-factory/ +pumlid: PSZB3OD034NHLa81Czwd6sCC39gVxEUWT1_ssLmTtQLqgR5fM7sTmFGtaV6TZu8prd0r6HtQaMKqAZLk1XjT_E6qgPUZfyc0MdTgx0-8LuUn8ErFXdr98NypXxKyezKV categories: Creational tags: - Java diff --git a/adapter/README.md b/adapter/README.md index ea3baa7fa91c..66ca638267eb 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Adapter folder: adapter permalink: /patterns/adapter/ +pumlid: DSR14S8m30J0Lg20M7-wEMnDOiPMFDA9j0yyUEtUkzMHJTF7xI1NF4GSLzaxZtncgDVJgCPIpobzv0N2vOKtjgRHTziMI7KBcOXl10thfxB-Nz9dMJd71m00 categories: Structural tags: - Java diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md index e65f26d9a38a..75fe373fd301 100644 --- a/aggregator-microservices/README.md +++ b/aggregator-microservices/README.md @@ -3,6 +3,7 @@ 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 diff --git a/api-gateway/README.md b/api-gateway/README.md index 23014ae0b8af..93b975e131d3 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -3,6 +3,7 @@ layout: pattern title: API Gateway folder: api-gateway permalink: /patterns/api-gateway/ +pumlid: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0 categories: Architectural tags: - Java diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 2d99820c5d82..b96c1d77a925 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -3,6 +3,7 @@ layout: pattern title: Async Method Invocation folder: async-method-invocation permalink: /patterns/async-method-invocation/ +pumlid: TSdB3SCW303GLTe1mFTkunWhk0A3_4dKxTi5UdlIUuhIoCPfuz4Zjhy03EzwIlGyqjbeQR16fJL1HjuOQF362qjZbrFBnWWsTPZeFm3wHwbCZhvQ4RqMOSXIuA1_LzDctJd75m00 categories: Concurrency tags: - Java diff --git a/bridge/README.md b/bridge/README.md index 49dad14e46fa..6c1e70631578 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -3,6 +3,7 @@ layout: pattern title: Bridge folder: bridge permalink: /patterns/bridge/ +pumlid: BSR14SCm20J0Lf82BFxf1akCJ4R26ZZYzkE7zxLljJgoIVfu7S2A3v7pLRhYo3r3l9u6CPHwJjAH5uETllpZhKbejsqn86v1a-CExQwj2mdgqv8-oyev_W00 categories: Structural tags: - Java diff --git a/builder/README.md b/builder/README.md index 5d1f3d24d469..6a661502f1ee 100644 --- a/builder/README.md +++ b/builder/README.md @@ -3,6 +3,7 @@ layout: pattern title: Builder folder: builder permalink: /patterns/builder/ +pumlid: DSR94O0m2030LhG0mzzkC64KXs26GzlNZw_TcRLADagJwOWOlW8OFcNdE79B9wkN1ccKUdLWoGS33KwySMdalEioC89C7Jhw5zYIfNrIrFybhPUHNLu0 categories: Creational tags: - Java diff --git a/business-delegate/README.md b/business-delegate/README.md index e6e2491222e0..8e6e3456c410 100644 --- a/business-delegate/README.md +++ b/business-delegate/README.md @@ -3,6 +3,7 @@ layout: pattern title: Business Delegate folder: business-delegate permalink: /patterns/business-delegate/ +pumlid: POl13SCm3CHMQGU8zUysgYCuBcJ5a4x9-l6_Fu84tzsgvYxf-Zg06HyYvxkqZYE_6UBrD8YXr7DGrxmPxFJZYxTTeZVR9WFY5ZGu5j2wkad4wYgD8IIe_xQaZp9pw0C0 categories: Business Tier tags: - Java diff --git a/caching/README.md b/caching/README.md index 2b89d0559e96..6432ffcea26e 100644 --- a/caching/README.md +++ b/caching/README.md @@ -3,6 +3,7 @@ layout: pattern title: Caching folder: caching permalink: /patterns/caching/ +pumlid: DSRB4OKm2030LhG0m_rrWyWaE0bc-6ZxpujxsbMKUXwSrfSMCVq7OFYKAj5oJsUZIuCr2bq3fEU3WGOdthWTx59rcnZ1fWu3_GqGKXEjm47VIzeeCqV_0m00 categories: Other tags: - Java diff --git a/callback/README.md b/callback/README.md index a408fd5e0bd5..278aa9b0aa5a 100644 --- a/callback/README.md +++ b/callback/README.md @@ -3,6 +3,7 @@ layout: pattern title: Callback folder: callback permalink: /patterns/callback/ +pumlid: FSVB4S8m30N0Lg20M7UwUL4qYOciUFGXxSE9s-wp6sjjKgwF8tF6YyXnjxtdKMk5E5-MOjdu6jIrRYIStlXWsIJwRij4fhW53SGFn51TmIT9yZ-jVBHPGxy0 categories: Other tags: - Java diff --git a/chain/README.md b/chain/README.md index ef18f6f64ca5..5ab4b2256909 100644 --- a/chain/README.md +++ b/chain/README.md @@ -3,6 +3,7 @@ layout: pattern title: Chain of responsibility folder: chain permalink: /patterns/chain/ +pumlid: 9SR13SCm20NGLTe1OkxTXX0KKzd4Wa-pVYlrdTxJN4OTMZ4U7LZv8Wg-ssdejLTgoELGHvDhaesw6HpqvWzlXwQTlYq6D3nfSlv2qjcS5F9VgvXjrHnV categories: Behavioral tags: - Java diff --git a/command/README.md b/command/README.md index a5478394cda1..9fb568f6216d 100644 --- a/command/README.md +++ b/command/README.md @@ -3,6 +3,7 @@ layout: pattern title: Command folder: command permalink: /patterns/command/ +pumlid: DSgn4OCm30NGLM00h3xR25i7vYpXaxx2-g59zugtTgiZcwIFvGHcV8YSdt9qdBbdYDVR88PIRwK-yc6mqyLVtff4FsoR38XRa7Aye3SgMoD1_RkaQvcfumS0 categories: Behavioral tags: - Java diff --git a/composite/README.md b/composite/README.md index 8b980292d9ce..fce6ed6af780 100644 --- a/composite/README.md +++ b/composite/README.md @@ -3,6 +3,7 @@ layout: pattern title: Composite folder: composite permalink: /patterns/composite/ +pumlid: HSf13eCm30NHgy01YFUzZGaM62LEP7-NwvTTT_EaMTLgoqFIst81Cpv4payv5LVk6U9r6CHGwkYaBHy6EztyvUsGqDEsoO2u1NMED-WTvmY5aA3-LT9xcTdR3m00 categories: Structural tags: - Java diff --git a/dao/README.md b/dao/README.md index 785a1c362a58..b1b655edf96f 100644 --- a/dao/README.md +++ b/dao/README.md @@ -3,6 +3,7 @@ layout: pattern title: Data Access Object folder: dao permalink: /patterns/dao/ +pumlid: 5SR14OKW30N0LhG0oVrt4o6ZE12Ov4NR_thQNQlc5aN2sd82qtz4naywAixOmyNoK8WYvT6fjdWOR7JnpLiHhuTkam4nTUhiRwZm847-J64zpUZj3m00 categories: Persistence Tier tags: - Java diff --git a/data-mapper/README.md b/data-mapper/README.md index 075e8eecef15..362f19c51d14 100644 --- a/data-mapper/README.md +++ b/data-mapper/README.md @@ -3,6 +3,7 @@ layout: pattern title: Data Mapper folder: data-mapper permalink: /patterns/data-mapper/ +pumlid: JShB3OGm303HLg20nFVjnYGM1CN6ycTfVtFSsnjfzY5jPgUqkLqHwXy0mxUU8wuyqidQ8q4IjJqCO-QBWGOtVh5qyd5AKOmW4mT6Nu2-ZiAekapH_hkcSTNa-GC0 categories: Persistence Tier tags: - Java diff --git a/decorator/README.md b/decorator/README.md index 63795114c798..e65b0eb9e1c7 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Decorator folder: decorator permalink: /patterns/decorator/ +pumlid: HSV14SCm20J0Lk82BFxf1YF6LaP26ZZizfDVVhjRC-bPDRs_Bc35cyZvAMV3bKU6kao36ehCGQtdms2d3z-yLursshuOKBUWmV43LPNfZEcaaFzA-YWhH_y2 categories: Structural tags: - Java diff --git a/delegation/README.md b/delegation/README.md index e5c0c63760c4..b294607ec92a 100644 --- a/delegation/README.md +++ b/delegation/README.md @@ -3,6 +3,7 @@ layout: pattern title: Delegation folder: delegation permalink: /patterns/delegation/ +pumlid: JSV14GCX20NGLf82LkxfXbN69OFeu2VRVdBCxRsdUhLiac6F2rZxHHHybwwuyimjKQT37ANEGMfvCpZepHy-ccpjVYm697pJuFq3DJ7f39rEWlhNaZ7Aoc5V categories: Behavioral tags: - Java diff --git a/dependency-injection/README.md b/dependency-injection/README.md index 735f589b1555..b4ec12e6b082 100644 --- a/dependency-injection/README.md +++ b/dependency-injection/README.md @@ -3,6 +3,7 @@ layout: pattern title: Dependency Injection folder: dependency-injection permalink: /patterns/dependency-injection/ +pumlid: RSdB3SCW303GLPe1mFTkunWhSGG6-PEesxS3zFQajubIpyPf_NL6B7y363xra3XpJsUZgS4QbUO0wVbWeC65DvR6BeUMXH5iwZ3GVu36YxMnqgU8NamXKu63_aPD6tNbw5y0 categories: Behavioral tags: - Java diff --git a/double-checked-locking/README.md b/double-checked-locking/README.md index da1fdd1a2ee7..582e4aac657c 100644 --- a/double-checked-locking/README.md +++ b/double-checked-locking/README.md @@ -3,6 +3,7 @@ layout: pattern title: Double Checked Locking folder: double-checked-locking permalink: /patterns/double-checked-locking/ +pumlid: TSdH4SCW203GLTe1bFzkGv1J6qGFeLc_MI1_x-wzkv94uJ1vDVUrFm26LwxTMnonsMYgitgcEQ1BNEXeyCKVfiAxLqqBtTbqmy1z0ygCGpXHOpgv99bqTgt0JW-LmqPUCUGF categories: Concurrency tags: - Java diff --git a/double-dispatch/README.md b/double-dispatch/README.md index ae87208a2639..b0f2185f9a34 100644 --- a/double-dispatch/README.md +++ b/double-dispatch/README.md @@ -3,6 +3,7 @@ layout: pattern title: Double Dispatch folder: double-dispatch permalink: /patterns/double-dispatch/ +pumlid: NSbB3iCW303HgpG70Ezx6yTOWSeOv4zp_MRTtUZDCPGa6wV9gqTiVmCOtlKQqVDCPwEbmHgLreGXUMEWmGU_M1hxkBHiZ61JXud-1BILft1fmvz37JZetshQh3kd_000 categories: Other tags: - Java diff --git a/event-aggregator/README.md b/event-aggregator/README.md index ac07869e7a89..ce7f358de9f8 100644 --- a/event-aggregator/README.md +++ b/event-aggregator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Event Aggregator folder: event-aggregator permalink: /patterns/event-aggregator/ +pumlid: PSf13iCW30NHgxG70Ezx6uTOX0eCih-JwvTzTwEdUJSjFKu9wwyBMFuXCdvoRRZY21ShKo6ANEQWrkDXiD6NRqwdUAkQ5WDYwZJOTv3SUqzSgqbbp0qeVvZ3Hbun-Wy0 categories: Structural tags: - Java diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md index 843e4c2684fe..0f698273b996 100644 --- a/event-driven-architecture/README.md +++ b/event-driven-architecture/README.md @@ -3,6 +3,7 @@ layout: pattern title: Event Driven Architecture folder: event-driven-architecture permalink: /patterns/event-driven-architecture/ +pumlid: TOhH3SCW30LNQGS0_tSRnrZ15H1adfFromBzkfFktZQaHT7mzgh0N1yYvoUVXXf7B7Mv1dGWozN9MZmCTlhopQdeidEaoO3wMDHvRI6zzvwAssPYbsfGGRYIGlxN7DxpZDv- categories: Architectural tags: - Java diff --git a/execute-around/README.md b/execute-around/README.md index f669f18ff0f3..e0ae128b537f 100644 --- a/execute-around/README.md +++ b/execute-around/README.md @@ -3,6 +3,7 @@ layout: pattern title: Execute Around folder: execute-around permalink: /patterns/execute-around/ +pumlid: NSZ14G8n20NGLhI0XBlT865suoGa0n_NylNixSsxTvEHJTF7xGHsF8YShtfqdFdCK9TbK4ELDQcFl1ZizE8tbwRH3okR0NKBcXm_a7vK4bhOLreZXVnLJPzrvnnV categories: Other tags: - Java diff --git a/facade/README.md b/facade/README.md index c416552c7505..22ccd69113c0 100644 --- a/facade/README.md +++ b/facade/README.md @@ -3,6 +3,7 @@ layout: pattern title: Facade folder: facade permalink: /patterns/facade/ +pumlid: BSP15eCm20N0gxG7CEoz3ILKqvTW7dpq-hhehERTJ7fMJU-l7PYn4ZbVPMlOyvEXBeT13KMEGQtdnM2d7v-yL8sssJ8PKBUWmV64lYnSbHJoRqaVPUReDm00 categories: Structural tags: - Java diff --git a/factory-kit/README.md b/factory-kit/README.md index c257010479bd..b47cbff76ba4 100644 --- a/factory-kit/README.md +++ b/factory-kit/README.md @@ -3,6 +3,7 @@ layout: pattern title: Factory Kit folder: factory-kit permalink: /patterns/factory-kit/ +pumlid: JST15i8m20N0g-W14lRU1YcsQ4BooCS-RwzBTpDNSscvQKQx7C1SDwBWi-w68--vD6Gur55bTBAM9uE3dlpcikcotSjaGCCNTLu_q8C58pxbPI25_Bzcz3gpjoy0 categories: Creational tags: - Java diff --git a/factory-method/README.md b/factory-method/README.md index 05549cf4f270..a444ffbd8ae4 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -3,6 +3,7 @@ layout: pattern title: Factory Method folder: factory-method permalink: /patterns/factory-method/ +pumlid: NSZB3G8n30N0Lg20n7UwCOxPP9MVx6TMT0zdRgEvjoazYeRrMmMsFuYChtmqr7Y6gycQq8aiQr3hSJ7OwEGtfwBUZfas0shJQR3_G2yMBFkaeQYha4B-AeUDl6FqBm00 categories: Creational tags: - Java diff --git a/feature-toggle/README.md b/feature-toggle/README.md index 51747ac09bdf..3bb91ad5ae7d 100644 --- a/feature-toggle/README.md +++ b/feature-toggle/README.md @@ -3,6 +3,7 @@ layout: pattern title: Feature Toggle folder: feature-toggle permalink: /patterns/feature-toggle/ +pumlid: NSZ14G8X30NGLhG0oDrk8XjPd12OvCTjNy_UthpxiAPvIBhUJc37WyZvgdtWp6U6U5i6CTIs9WtDYy5ER_vmEIH6jx8P4BUWoV43lOIHBWMhTnKIjB-gwRFkdFe5 categories: Behavioral tags: - Java diff --git a/fluentinterface/README.md b/fluentinterface/README.md index 767792da79ab..8bf43d97433b 100644 --- a/fluentinterface/README.md +++ b/fluentinterface/README.md @@ -3,6 +3,7 @@ layout: pattern title: Fluent Interface folder: fluentinterface permalink: /patterns/fluentinterface/ +pumlid: NOj93eCm302_KXv0VEzlN6F0bMCYB_3zvjpRQ3IpY97MnkNwEZD7l04SdtP8dlMfOAVBaYqRNHr4wy54Xo_Uk6uSSjWwC9FT0Zh61DYrPY_pyXs9WPF-NIllRLJN7m00 categories: Other tags: - Java diff --git a/flux/README.md b/flux/README.md index 7ac312c443d2..e36b73900e9d 100644 --- a/flux/README.md +++ b/flux/README.md @@ -3,6 +3,7 @@ layout: pattern title: Flux folder: flux permalink: /patterns/flux/ +pumlid: 7SP14eCm20NGg-W13FlU1YFLE0GpyAazVZk-rPkRLSrDqdKwW14l8kUxx0r7hXdYzJA8eTIhKzEy6UnqyeUNJQBjjWm6n2seS_n3Ryql2UgJajxBoAu_ categories: Presentation Tier tags: - Java diff --git a/flyweight/README.md b/flyweight/README.md index a98dced8e4e3..1e71f1c027a2 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -3,6 +3,7 @@ layout: pattern title: Flyweight folder: flyweight permalink: /patterns/flyweight/ +pumlid: HSV94S8m3030Lg20M7-w4OvYAoCh7Xtnq3ty-Eq-MQlaJcdow17JNm26gpIEdkzqidffa4Qfrm2MN1XeSEADsqxEJRU94MJgCD1_W4C-YxZr08hwNqaRPUQGBm00 categories: Structural tags: - Java diff --git a/front-controller/README.md b/front-controller/README.md index a462a08e0ab6..c832674dc13b 100644 --- a/front-controller/README.md +++ b/front-controller/README.md @@ -3,6 +3,7 @@ layout: pattern title: Front Controller folder: front-controller permalink: /patterns/front-controller/ +pumlid: PSlB3OGm303HLfO24j-t6nCC13bEvC_IFk6yjz6JPgbIE3OAvS_fFkmBe7Zde_ePQnXfwU8adajlK3bkT5Iuy8Tf8wk7f87kf6BGq6R0hlD8xwQTUG9v-SCSslA8nWy0 categories: Presentation Tier tags: - Java diff --git a/half-sync-half-async/README.md b/half-sync-half-async/README.md index 8a091f8133a3..55891e770601 100644 --- a/half-sync-half-async/README.md +++ b/half-sync-half-async/README.md @@ -3,6 +3,7 @@ layout: pattern title: Half-Sync/Half-Async folder: half-sync-half-async permalink: /patterns/half-sync-half-async/ +pumlid: RScv3SCm3030LU819FRPXg5fIm552tnYPFiyjRi3RkbAaYkdoQr5JBy369vrxz7oaSv6XmPhL3e6TCaJ0msU-CAoilTToyG8DdKOw5z0GzcAlvNAN_WZSD1brBHHPmxv0000 categories: Concurrency tags: - Java diff --git a/hexagonal/README.md b/hexagonal/README.md index b1d0a7948ee2..33c2ba9cbd0b 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -3,6 +3,7 @@ layout: pattern title: Hexagonal Architecture folder: hexagonal permalink: /patterns/hexagonal/ +pumlid: HSTB4W8X30N0g-W1XkozpPD90LO8L3wEnzUTk-xxq2fvSfhSUiJs1v7XAcr4psSwMrqQh57gcZGaBmICNdZZEDb7qsCZWasT9lm7wln1MmeXZlfVIPjbvvGl categories: Architectural tags: - Java diff --git a/intercepting-filter/README.md b/intercepting-filter/README.md index 7d53472a077f..4981299ad27b 100644 --- a/intercepting-filter/README.md +++ b/intercepting-filter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Intercepting Filter folder: intercepting-filter permalink: /patterns/intercepting-filter/ +pumlid: RSfB3i8m303Hgy014k-vZN5DQkIuaJ_q-fGzkz7JtCL8Q-DolUsPAnu0ZcSVadizAzZfi6JBJiS4qJenqU6D7smRXmnh2pFPBM1YN05o_KwyKcoqb-ZFEEcVz_BPLqtz0W00 categories: Behavioral tags: - Java diff --git a/interpreter/README.md b/interpreter/README.md index 87c1c47f7892..7a09ab0c7a09 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Interpreter folder: interpreter permalink: /patterns/interpreter/ +pumlid: JSf13eCm30NHgz034E-vZGaM62Kcih_BzQ6xxjv8yr6hBJT9RzC1Z5Y8dE-oAuvSCyJhPH13gLSdRNapsEdaBy-RXEus3mR4BQXpl21zVnykFmlgVvVqNaRszW00 categories: Behavioral tags: - Java diff --git a/iterator/README.md b/iterator/README.md index d6be7758dda1..91cc64d8d801 100644 --- a/iterator/README.md +++ b/iterator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Iterator folder: iterator permalink: /patterns/iterator/ +pumlid: FSV13OGm30NHLg00uljsOu85HeaJsTzB-yjfBwCtgrfjUKXwMovWneV8-IcduiezGxmEWnXA7PsqvSDWfvk_l1qIUjes6H2teCxnWlGDOpW9wdzAUYypU_i1 categories: Behavioral tags: - Java diff --git a/layers/README.md b/layers/README.md index 8e8eda36666e..d62c6b6b71e9 100644 --- a/layers/README.md +++ b/layers/README.md @@ -3,6 +3,7 @@ layout: pattern title: Layers folder: layers permalink: /patterns/layers/ +pumlid: BSR13OCm30NGLSe0n7UsCS62L8w9x6yGszD3t-bDpQhc9kdwEO0H2v7pNVQ68zSCyNeQn53gsQbftWns-lB5yoRHTfi70-8Mr3b-8UL7F4XG_otflOpi-W80 categories: Architectural tags: - Java diff --git a/lazy-loading/README.md b/lazy-loading/README.md index d40061293531..4b7a580c3f6f 100644 --- a/lazy-loading/README.md +++ b/lazy-loading/README.md @@ -3,6 +3,7 @@ layout: pattern title: Lazy Loading folder: lazy-loading permalink: /patterns/lazy-loading/ +pumlid: LSXB3W8X303Gg-W1e7jlqu66gIc5zED4JwzRTo_lpjeaEwN9xOpO_W0mlEhWEFD89sjBWpHgMnDOyi90WoU-i7Ho7besHf2fmqJ_0GG_xo8BE-i0YlONDMtMdLE- categories: Other tags: - Java diff --git a/mediator/README.md b/mediator/README.md index c7a0478c8c66..0e9f9c21659b 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Mediator folder: mediator permalink: /patterns/mediator/ +pumlid: FSV14SCm20J0Lk82BFxf1akCJKOW3JhizfDNVhkRUktP9AE_Bc2kDr7mKqx5bKSkYJeSuYXr66dFXy517xvvRxBqz7qo8E6BZDSFPDAKCO84zP-IOMMczIy0 categories: Behavioral tags: - Java diff --git a/memento/README.md b/memento/README.md index 463b5fec0836..f4384932980f 100644 --- a/memento/README.md +++ b/memento/README.md @@ -3,6 +3,7 @@ layout: pattern title: Memento folder: memento permalink: /patterns/memento/ +pumlid: DSgn4OCm30NGLM00h3xR2AC3SvRiaxx2-g59zugtDgiz3qdlomNC-10vF-Lik7BF4A_388PIXrBh-J3OwUOlRuT4EssR38XRa7Ay81Lz_o11_RkaQvcf_GS0 categories: Behavioral tags: - Java diff --git a/message-channel/README.md b/message-channel/README.md index 8aebd01576f4..aa357ac0c390 100644 --- a/message-channel/README.md +++ b/message-channel/README.md @@ -3,6 +3,7 @@ layout: pattern title: Message Channel folder: message-channel permalink: /patterns/message-channel/ +pumlid: NSZB3SCm203GLTe1RExTXX1akm9YyMdMRy-zFRtdCf8wkLmUCtF72y3nxcFbhAE2dIvBjknqAIof6nCTtlZ1TdAiOMrZ9hi5ACOFe1o1WnjDD6C1Jlg_NgvzbyeN categories: Integration tags: - Java diff --git a/model-view-controller/README.md b/model-view-controller/README.md index bc96f7ab1e01..9907b98bddc2 100644 --- a/model-view-controller/README.md +++ b/model-view-controller/README.md @@ -3,6 +3,7 @@ layout: pattern title: Model-View-Controller folder: model-view-controller permalink: /patterns/model-view-controller/ +pumlid: ROl13SCm201NQGUm-NSRQgE42h258Lw_wR-_qvtkoTOaEwNBuuoOwmNWkEl1SUOx5taR5cHHsr1WoOs13X-yi7HQV5YP645k2nJN3Q2ZavIBQPVVwqFajXJjVwdfMcUgV040 categories: Presentation Tier tags: - Java diff --git a/model-view-presenter/README.md b/model-view-presenter/README.md index a3b921ce4f04..04a1fa559521 100644 --- a/model-view-presenter/README.md +++ b/model-view-presenter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Model-View-Presenter folder: model-view-presenter permalink: /patterns/model-view-presenter/ +pumlid: ROlR3SGW3C1MkGu0-RzjKeXQJWcWFChwPO3xispvQBrmL0hbp-q-xGkWkFBL_8upZBICxjGzbo7GE1OwAlpmmLJ9sjNJH7VIRY1e6q169KvFevMcakrtI_BoD-HGoJE4Nm00 categories: Presentation Tier tags: - Java diff --git a/monad/README.md b/monad/README.md index 41edd3d92b4f..bf6ee58b891e 100644 --- a/monad/README.md +++ b/monad/README.md @@ -3,6 +3,7 @@ layout: pattern title: Monad folder: monad permalink: /patterns/monad/ +pumlid: 9SR13SCm20NGLPe1OkxTXjWeSMMm1Pza_LRgExsjMntP97syBc35cyZvAMV7bKU6U9q6CPGwbVh8Xy5E7xvvRnBzj7qn86v1ol4BwJHk9AZ_bNGjAtLy0G00 categories: Other tags: - Java diff --git a/monostate/README.md b/monostate/README.md index 3576dc659313..8c47b5da4d2e 100644 --- a/monostate/README.md +++ b/monostate/README.md @@ -3,6 +3,7 @@ layout: pattern title: MonoState folder: monostate permalink: /patterns/monostate/ +pumlid: HSV14OGm20NGLjO23FVj1YEZsGaa0nzjVxrvUszfLdlkaju_9p3ZI-HybwFXp2r3l0w364eTIgtdpM2d7r-yxXBji7Ko86v1ol60TDW8C8G4zLr9rp9J-ny0 categories: Creational tags: - Java diff --git a/multiton/README.md b/multiton/README.md index 0462ff0ecc48..a1154e7bb945 100644 --- a/multiton/README.md +++ b/multiton/README.md @@ -3,6 +3,7 @@ layout: pattern title: Multiton folder: multiton permalink: /patterns/multiton/ +pumlid: FST14i8m20NGg-W16lRUXgPCYnD81Zxs-hfozzvJlOywf68yBc3bYoZuRgVYghrIea-7E5gVHZhgPd3Gcp-y7P9w-hOOaF0au_o1h0OKqqdG_saLrbRP-080 categories: Creational tags: - Java diff --git a/mute-idiom/README.md b/mute-idiom/README.md index bb674b648b48..5c3dbf10b9af 100644 --- a/mute-idiom/README.md +++ b/mute-idiom/README.md @@ -3,6 +3,7 @@ layout: pattern title: Mute Idiom folder: mute-idiom permalink: /patterns/mute-idiom/ +pumlid: JSf13iCm20NHgxG7iDdtDjH62PKX5luarq-MtSsJvgtUHdR96AyTcEj357pLJR7dDvT4EnpYgEqmqf4NWuD-V7BfidJpCXcGy4N6wmcoX1Jj-lo2ziUQONMcZHi0 categories: Other tags: - Java diff --git a/mutex/README.md b/mutex/README.md index 84755872f56f..78cda9060de9 100644 --- a/mutex/README.md +++ b/mutex/README.md @@ -3,6 +3,7 @@ layout: pattern title: Mutex folder: mutex permalink: /patterns/mutex/ +pumlid: 9SR13OCm30NGLSe0n7UsCS62LB69x6zWV2hrdTxKhFRS9Br_3c34GkHybxtXo3L3l9u6CPHwAhMUDuETldpnl4cqtUR1WBW5ASSlf0bvI53_A-bQHcf_0G00 categories: Concurrency tags: - Java diff --git a/naked-objects/README.md b/naked-objects/README.md index 66e6ac2b0224..14391dd40622 100644 --- a/naked-objects/README.md +++ b/naked-objects/README.md @@ -3,6 +3,7 @@ layout: pattern title: Naked Objects folder: naked-objects permalink: /patterns/naked-objects/ +pumlid: LSX15i8W30N0g-W187jlaq9igH1uoO_r-BfrDs_kJKkFAc7zTW3B7qJ6LzuRZjZ2nSfKY2ANEQZrk1XiTFARKnLlkwR5W9Ww3VOVIFabDStjb08dGVcVz6mVX4aE6td5w5y0 categories: Architectural tags: - Java diff --git a/null-object/README.md b/null-object/README.md index 0ed28a0af33c..bfaaeac660f5 100644 --- a/null-object/README.md +++ b/null-object/README.md @@ -3,6 +3,7 @@ layout: pattern title: Null Object folder: null-object permalink: /patterns/null-object/ +pumlid: JSV14SCm20J0Lk829Fxf1cF6bWSX3JhYzfDdVhjRSx4yDCDU5p3NcoZugMV3bNik3HaETLGPdPhbm-2WcpzS3btjz38PqF15dTSFv6bMndwhW1Jo_vhHwynkNm00 categories: Behavioral tags: - Java diff --git a/object-pool/README.md b/object-pool/README.md index cf36d98809e7..15fee51aa0fc 100644 --- a/object-pool/README.md +++ b/object-pool/README.md @@ -3,6 +3,7 @@ layout: pattern title: Object Pool folder: object-pool permalink: /patterns/object-pool/ +pumlid: JSV94SCm2030Lk829Fxf1cF6bWU1XYDkFtdcjxiD9Qc3o-LrPQvu0pW-_HnvrLx1JgR9cfrimf1wCD7XnW-sWsESsXPcicl0nFW1RB-PiYqp0KxwVo-VVTMKBm00 categories: Creational tags: - Java diff --git a/observer/README.md b/observer/README.md index 6fbe3cdab9fd..6a9e3f584279 100644 --- a/observer/README.md +++ b/observer/README.md @@ -3,6 +3,7 @@ layout: pattern title: Observer folder: observer permalink: /patterns/observer/ +pumlid: FSkn4OGm30NHLg00hFow4KO3PcpP8tr1-pYwx6smQz5Suv2mkbp0y1-HyPlEWYlsSB7S5Q98kJSgDLu66ztyy7Q8brEtmO2OEZNs2Uhxl9u9GVv72cjfHAiV categories: Behavioral tags: - Java diff --git a/page-object/README.md b/page-object/README.md index b4f8246f117f..2219a077c282 100644 --- a/page-object/README.md +++ b/page-object/README.md @@ -3,6 +3,7 @@ layout: pattern title: Page Object folder: page-object permalink: /patterns/page-object/ +pumlid: JSV14OGW30NGLjO28FVj9iOCua1Wme-sxnxtzjvMJLeS6ju-9p3NbyZvoQNYZ3sMkWo36hACJhN5ms2dYszEXwvQB4q6r6rHv_K3JIwQndwfW1Jo_npUyupUNW00 categories: Testing tags: - Java diff --git a/poison-pill/README.md b/poison-pill/README.md index 0815b376edf8..8f673ad49cf4 100644 --- a/poison-pill/README.md +++ b/poison-pill/README.md @@ -3,6 +3,7 @@ layout: pattern title: Poison Pill folder: poison-pill permalink: /patterns/poison-pill/ +pumlid: JSZ14SCm20NHLf82BExfXiYCJGOX3NpYzkDZRllsgTwjTgcmnmciV145N-rGdFMkbEZJ8OxMvo2rkXWSzE4lRxka7huj1YGyQN3UGMjgpdkh6Gdwlrl5QAk6_G00 categories: Other tags: - Java diff --git a/private-class-data/README.md b/private-class-data/README.md index 981208fa3f0e..061cc9e7713a 100644 --- a/private-class-data/README.md +++ b/private-class-data/README.md @@ -3,6 +3,7 @@ layout: pattern title: Private Class Data folder: private-class-data permalink: /patterns/private-class-data/ +pumlid: RShR3SCm243HLTe1RFwx3S4eeSB4uf6itmpGlwkZ-nOZhS7b-ZeoLtm07E--InwrLR3JQScMdSu9edLZeiCNBso3GtPh2pFPBM1YF07BvSBaHeeHRJm_SD8VxkMphvhw0m00 categories: Other tags: - Java diff --git a/producer-consumer/README.md b/producer-consumer/README.md index 1bb84c35fbb2..b3cb56af12aa 100644 --- a/producer-consumer/README.md +++ b/producer-consumer/README.md @@ -3,6 +3,7 @@ layout: pattern title: Producer Consumer folder: producer-consumer permalink: /patterns/producer-consumer/ +pumlid: PSjB3iCW303HgxG70Ezx6zTO2HKso9_a-c7VtUX9y-vA8nkdZTSPiVm3O7ZNeyUPttGscXgiKMaAz94t1XhyyCBIsFkXPM44cpe8-WvODbiIMzcdfspXe7-jQL9NodW0 categories: Concurrency tags: - Java diff --git a/property/README.md b/property/README.md index 0ac5c7a6c682..c1502f75cfd2 100644 --- a/property/README.md +++ b/property/README.md @@ -3,6 +3,7 @@ layout: pattern title: Property folder: property permalink: /patterns/property/ +pumlid: FSV13OCm30NGLTe1YEziumOBKYMEPN-3s9wUUdlltRJst2Izlmx0OYLolihUSEGdGxnEXIXAdODQpul1Jby-UTaasgwBCI2kGOFZ1pAV9ewR1FMVaZwAvUWF categories: Creational tags: - Java diff --git a/prototype/README.md b/prototype/README.md index 632daca931aa..fe9e17917685 100644 --- a/prototype/README.md +++ b/prototype/README.md @@ -3,6 +3,7 @@ layout: pattern title: Prototype folder: prototype permalink: /patterns/prototype/ +pumlid: HSV13OCm30NGLM00udktCS62eCInxE-YRj_UUdjlRLfx7fBUbmkmU14vF-Lik7BF4AzJ8OfIvw3Mys6mqyrltWw9Tkfc38XhqE3uWSmd9Zuc9AZ_bVHHB4V_0W00 categories: Creational tags: - Java diff --git a/proxy/README.md b/proxy/README.md index a3e03708e328..1c22c071013d 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -3,6 +3,7 @@ layout: pattern title: Proxy folder: proxy permalink: /patterns/proxy/ +pumlid: 9SR13OCm30NGLM00udktCS62eCI9x6yesrEfx_Jcehd69c5rEe3X7oBZE-q5HwpXOhahH95oRrHgt0msEldYPHClkow30J5rQko_qB3-VKYG_qjXBOrezGK0 categories: Structural tags: - Java diff --git a/publish-subscribe/README.md b/publish-subscribe/README.md index 6a5b2dfa8f51..3265e42eae04 100644 --- a/publish-subscribe/README.md +++ b/publish-subscribe/README.md @@ -3,6 +3,7 @@ layout: pattern title: Publish Subscribe folder: publish-subscribe permalink: /patterns/publish-subscribe/ +pumlid: PSZB3SCm203GLTe1RExT1XCKKs5YyMdMR--zFRsd66aTNAwFcRdZ1U1uzrDorgXWfykIBJjT2qJhnaI7Dtwm7HnoMjkOoMu12-C7s3LKOhQe4UGo63ZfVtlvwhkMVW40 categories: Integration tags: - Java diff --git a/reactor/README.md b/reactor/README.md index b9ba98948064..7fd3982ad327 100644 --- a/reactor/README.md +++ b/reactor/README.md @@ -3,6 +3,7 @@ layout: pattern title: Reactor folder: reactor permalink: /patterns/reactor/ +pumlid: DSR14OGm20NGLjO23FVj1f7Hx2Ga0nzjVxtuJc-f9YrtJM-V4vZn9NA-or5nvfQXBiEWXYAZKsrvCzZfnnUlkqOzR9qCg5jGvtX2hYmOJWfvNz9xcTdR7m00 categories: Concurrency tags: - Java diff --git a/reader-writer-lock/README.md b/reader-writer-lock/README.md index 40b71136167b..556b9cd8541f 100644 --- a/reader-writer-lock/README.md +++ b/reader-writer-lock/README.md @@ -3,6 +3,7 @@ layout: pattern title: Reader Writer Lock folder: reader-writer-lock permalink: /patterns/reader-writer-lock/ +pumlid: RSZB4S8m303HLg00MtUw4R8cCP5bZpwuVL80jttxx4gIZTFaSKOiVm4OxdhqEFETpaPJWpKgpG5TScEWmGU_M1fxFxGiZ61JXu5-1nXZOolR-gqYaoxWe3-xfeswSiWF categories: Concurrency tags: - Java diff --git a/repository/README.md b/repository/README.md index 67b3ea44e828..501f39f9c60a 100644 --- a/repository/README.md +++ b/repository/README.md @@ -3,6 +3,7 @@ layout: pattern title: Repository folder: repository permalink: /patterns/repository/ +pumlid: JSV13OCm30NGLM00udktCS42eyI9xE-YRjyUUtjlLQij3qblomNCU14vF-LKNBbdYDTX44EfevEsV1ZiTFERjqD2Jzic0-8Mr3b-89SvGZ7yGuBwrvBUoypUlW00 categories: Persistence Tier tags: - Java diff --git a/resource-acquisition-is-initialization/README.md b/resource-acquisition-is-initialization/README.md index 821f220d74de..df7ee294b211 100644 --- a/resource-acquisition-is-initialization/README.md +++ b/resource-acquisition-is-initialization/README.md @@ -3,6 +3,7 @@ layout: pattern title: Resource Acquisition Is Initialization folder: resource-acquisition-is-initialization permalink: /patterns/resource-acquisition-is-initialization/ +pumlid: ZShR3S8m343HLUW0YV_PnhXMQvGumOzMOdhA1lqxkhgBABLSEQqzzeZfJm33isuIUxxIsMXei4QbqK5QdXXeyCO3oyekcvQ94MpgqD4lWB6FDEA2z4bn2HbQn8leHMponNy13hgvrhHUP_Rs0m00 categories: Other tags: - Java diff --git a/semaphore/README.md b/semaphore/README.md index 46ccd7b8ea22..071e061a7c00 100644 --- a/semaphore/README.md +++ b/semaphore/README.md @@ -3,6 +3,7 @@ layout: pattern title: Semaphore folder: semaphore permalink: /patterns/semaphore/ +pumlid: HSV14SCm20J0Lk82BFxf1ikCfOn06ZZizfDVVhjRjphobFJnQi2ADv7pKwwEbaU6U9q6CPGwbVh8Xy5E7xvvFoNwPVjYGDo2bEC72b5URRgGeFvNqhMirF45 categories: Concurrency tags: - Java diff --git a/servant/README.md b/servant/README.md index 3e82ab2cf904..d14d35edfcd7 100644 --- a/servant/README.md +++ b/servant/README.md @@ -3,6 +3,7 @@ layout: pattern title: Servant folder: servant permalink: /patterns/servant/ +pumlid: DSkn4O0m20NGLNG0G-ys63cDbv0SV7HzRUnUy-QYkSOkONKwWU4haV6JZe8pjd2nt1MYIBatAZKU1XjTVFEoYvT3by60c3erzW_qdPiL9CY_KrXB8rfz0G00 categories: Structural tags: - Java diff --git a/service-layer/README.md b/service-layer/README.md index 9b685d4e3f4b..af393947f343 100644 --- a/service-layer/README.md +++ b/service-layer/README.md @@ -3,6 +3,7 @@ layout: pattern title: Service Layer folder: service-layer permalink: /patterns/service-layer/ +pumlid: LOl93SCm3C1MQGUmzUysgY8aAcJ5q96WszVV_aW2V8gHriRb-ZWoPxm07E--Inxrhc2dqv8jEvq3HEl6H8SFNjWs3jcjJSnaju21iG3MSmbnK_mkuwJ_qij7dpNq1m00 categories: Architectural tags: - Java diff --git a/service-locator/README.md b/service-locator/README.md index af4d8c3ac822..31d82b13f8bd 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Service Locator folder: service-locator permalink: /patterns/service-locator/ +pumlid: NSjB3iCm203HgxG7iDdtDeIWX0fZYqzo_MRTtUX9ynOZhPtBzNLchlW0EDxza3nhgs2dQScMdUO0qRenqU6B5xQTGmvh2pFPBM1WF07FSmbnqqcOqu6J_gsNZxvgw0y0 categories: Structural tags: - Java diff --git a/singleton/README.md b/singleton/README.md index 2a481f5c8e10..cd6fc131de66 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -3,6 +3,7 @@ layout: pattern title: Singleton folder: singleton permalink: /patterns/singleton/ +pumlid: HSV14SCm20J0Lk82BFxf1ikCh0n26ZZizfDVVhjRjwfvIhg-Bc35cyZvAQtZoYD3l4w364gTWxhcms2d3z-ydnAzsRuO4BUWmV43HRUcWcaagF-Lz55M3lq2 categories: Creational tags: - Java diff --git a/specification/README.md b/specification/README.md index 564830653d7f..dc47f4970e4c 100644 --- a/specification/README.md +++ b/specification/README.md @@ -3,6 +3,7 @@ layout: pattern title: Specification folder: specification permalink: /patterns/specification/ +pumlid: LSX14i8m20NGg-W16lRU1YcsE0d9mCTUNxVkthoxkVJQjQBVJc3bWoZuQeVXh6UbXao7EfhCGTRhOd3Gcp-yxPfs-BOOqF2amVa3vLAnbmd3ffD2_gTLZBPgz2y0 categories: Behavioral tags: - Java diff --git a/state/README.md b/state/README.md index f5cb189fde44..8e3256b42667 100644 --- a/state/README.md +++ b/state/README.md @@ -3,6 +3,7 @@ layout: pattern title: State folder: state permalink: /patterns/state/ +pumlid: 9SRH3O0m20N0LNG0ox_RO2LQqz867hg-9jxNpKLpZLt2wdG2mrSYuoST1MTiuMAvAqIHSczKQZmCDhhuvcKNBuSkWm4nTMhiNyZ141BaVocifH6jlW00 categories: Behavioral tags: - Java diff --git a/step-builder/README.md b/step-builder/README.md index bc636e37ae06..65d356c2e1a0 100644 --- a/step-builder/README.md +++ b/step-builder/README.md @@ -3,6 +3,7 @@ layout: pattern title: Step Builder folder: step-builder permalink: /patterns/step-builder/ +pumlid: LOZ93SCm3C1MQGQmzUysYYqaAcJ5q96i7t_x8KXkh4soKvfypeZfNm33fnuSP-xfPEtI88tQhW4i-M2WmGzlB9sS3oqJ8yZKOQ0lWOLPzcJfAoZQtwXfeyuSyW80 categories: Creational tags: - Java diff --git a/strategy/README.md b/strategy/README.md index f07397f678ee..697b6cc88c6a 100644 --- a/strategy/README.md +++ b/strategy/README.md @@ -3,6 +3,7 @@ layout: pattern title: Strategy folder: strategy permalink: /patterns/strategy/ +pumlid: FSV13OCm30NGLM00udktCS4AGOaJsTz5tRwSkBstLiqj3WbhombC_n0PtwbKdB67Y-MX44NAerDjSJFOwE8lRuTuBRfD1iJKgRC_88SnfFn8aD-ai9vczFO7 categories: Behavioral tags: - Java diff --git a/template-method/README.md b/template-method/README.md index ad972f06bce5..65381d0ce12e 100644 --- a/template-method/README.md +++ b/template-method/README.md @@ -3,6 +3,7 @@ layout: pattern title: Template method folder: template-method permalink: /patterns/template-method/ +pumlid: NSZ13SCW30NGLPe1mFTkuu0Lg6n0vZjPlpttzlIEFef6bN1zDM3jDv7paw-E5cTiyJ87P22NQTGr7WOxVVZcL6NtQwJ5WFZOPBn_88WjPKWoGPkL1EN_ShZb5QPV categories: Behavioral tags: - Java diff --git a/thread-pool/README.md b/thread-pool/README.md index 9806fa8e0b31..473494ef9349 100644 --- a/thread-pool/README.md +++ b/thread-pool/README.md @@ -3,6 +3,7 @@ layout: pattern title: Thread Pool folder: thread-pool permalink: /patterns/thread-pool/ +pumlid: JSV14SCW30J0Lk82GFzq8uF6a1624IUx_UIPt-xHhMXK2TTN0zP-4pa_-UfeSSOMBzCWXbpceAxnCDZfmpUdAhjVbXO3uhPfyFw1q5oufZMdag3yFuUFl6Be5m00 categories: Concurrency tags: - Java diff --git a/tolerant-reader/README.md b/tolerant-reader/README.md index be0085f2c571..5d1cf80fd22a 100644 --- a/tolerant-reader/README.md +++ b/tolerant-reader/README.md @@ -3,6 +3,7 @@ layout: pattern title: Tolerant Reader folder: tolerant-reader permalink: /patterns/tolerant-reader/ +pumlid: NSZ14SCm20NHLf829ExfXaYChGn26lZ4xSVdtFRjSrZJx9AkZnFOyI9olkenSEOxGxmjWnXgMvE6viLWfmz_kNI9SLZP38XRqEIuWx1Kd0t5XVjjGVj_DNtMdLD_ categories: Integration tags: - Java diff --git a/twin/README.md b/twin/README.md index 3795236bb528..092032a55f77 100644 --- a/twin/README.md +++ b/twin/README.md @@ -3,6 +3,7 @@ layout: pattern title: Twin folder: twin permalink: /patterns/twin/ +pumlid: 7SR13OCm30NGLUW0n7UsCS42eyH4zdUpFbNVwNtKQij3qjjo0ICs8kTPJiMLUuPuVGnYAFNff2qdWvrk_l9wIEXfws10t88wno-4gKQ2-az9xsLaRoy0 categories: Creational tags: - Java diff --git a/value-object/README.md b/value-object/README.md index 83223d8a2377..a8e707b055d3 100644 --- a/value-object/README.md +++ b/value-object/README.md @@ -3,6 +3,7 @@ layout: pattern title: Value Object folder: value-object permalink: /patterns/value-object/ +pumlid: LSZ13SCm20NGLTe1RExTXX2KECBOmfza_VRQszDxDnVBNJFiTG9pVOY2dteqdBdbqf3XK4ULqQbPFWmEklZcikjgXvV9W8Olwhn-e9ijjOpjKW4fv2zgHgypktq1 categories: Creational tags: - Java diff --git a/visitor/README.md b/visitor/README.md index c1e24a624c23..712abad87e34 100644 --- a/visitor/README.md +++ b/visitor/README.md @@ -3,6 +3,7 @@ layout: pattern title: Visitor folder: visitor permalink: /patterns/visitor/ +pumlid: DSR14OGm20NGLhG0mtsxmSWeJa8oyD7sTo_xJczLgoqFIM_B1Spu43c_vLHSkMU8rs4GGwcZaxPy6UnqyyFR8Q6dRPC1SGlg7B_Gew4OJeBwVqdlPMPlNm00 categories: Behavioral tags: - Java From 5c1a4f1caf8145ca8dba90c34a838de53dcd95f5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 31 Aug 2016 16:12:13 +0530 Subject: [PATCH 149/207] Added example that mocking frameworks use proxy pattern --- proxy/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proxy/README.md b/proxy/README.md index 1c22c071013d..a3cdbf788d7c 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -41,6 +41,7 @@ are several common situations in which the Proxy pattern is applicable * [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) * [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) +* Mocking frameworks Mockito, Powermock, EasyMock ## Credits From e425c2ef2f721600e14b59d67eb5ef27759113f0 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Wed, 31 Aug 2016 13:15:44 +0200 Subject: [PATCH 150/207] Add webhook for travis build failures to gitter Only the core group (private) gitter room --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index bcbad682743d..aed6ef420a0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,5 +18,11 @@ after_success: notifications: email: - iluwatar@gmail.com + webhooks: + urls: + - https://webhooks.gitter.im/e/3319623945358a093a6f + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always sudo: false # route the build to the container-based infrastructure for a faster build From 90c636abd312c40fee22c2bf7dc3c057e811ce75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 2 Sep 2016 21:28:45 +0300 Subject: [PATCH 151/207] Add missing license headers --- _scripts/postPumlsToServer.py | 23 +++++++++++++++++++ .../java/com/iluwatar/promise/Utility.java | 22 ++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/_scripts/postPumlsToServer.py b/_scripts/postPumlsToServer.py index 2f975d3d870a..3929b3c865aa 100644 --- a/_scripts/postPumlsToServer.py +++ b/_scripts/postPumlsToServer.py @@ -1,3 +1,26 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + import requests, glob, re, os # taken from here: http://stackoverflow.com/a/13641746 diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java index 8d5be2538f4f..d451600a359f 100644 --- a/promise/src/main/java/com/iluwatar/promise/Utility.java +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.promise; import java.io.BufferedReader; From be2c7fdb2bf4279c6482cb794c1389ae9e83b7e9 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Fri, 2 Sep 2016 23:15:38 +0200 Subject: [PATCH 152/207] Update URM version: 1.4.0 -> 1.4.1, fixes #492 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 777ec6b69c73..30234699313d 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 4.12.1 4.5.2 2.22 - 1.4.0 + 1.4.1 abstract-factory From 165d1f12986cd5d0735a4fb667a14dda905012ec Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Fri, 2 Sep 2016 23:54:52 +0200 Subject: [PATCH 153/207] Turn Error Tracing on when installing Travis currently errors and i cant reproduce locally, so this might help finding the culprit --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index aed6ef420a0e..c502e8a99e9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start +# default install command is just "mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V" +install: +- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e + after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh From fa52a7f77eb038cf7e67bf9dfa32089b1fec096b Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Sat, 3 Sep 2016 00:19:50 +0200 Subject: [PATCH 154/207] Run build with latest java 8 release --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index c502e8a99e9e..19d4614f4bd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,11 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh +addons: + apt: + packages: + - oracle-java8-installer + notifications: email: - iluwatar@gmail.com From ff23e90c4f6e08127c3b0b33539531f4c07e4724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 3 Sep 2016 08:43:35 +0300 Subject: [PATCH 155/207] Add puml for Promise pattern --- promise/etc/promise.urm.puml | 77 ++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 promise/etc/promise.urm.puml diff --git a/promise/etc/promise.urm.puml b/promise/etc/promise.urm.puml new file mode 100644 index 000000000000..d97871411aed --- /dev/null +++ b/promise/etc/promise.urm.puml @@ -0,0 +1,77 @@ +@startuml +package com.iluwatar.promise { + ~class PromiseSupport { + - COMPLETED : int {static} + - FAILED : int {static} + - RUNNING : int {static} + - exception : Exception + - lock : Object + - state : int + - value : T + ~ PromiseSupport() + + cancel(mayInterruptIfRunning : boolean) : boolean + ~ fulfill(value : T) + ~ fulfillExceptionally(exception : Exception) + + get() : T + + get(timeout : long, unit : TimeUnit) : T + + isCancelled() : boolean + + isDone() : boolean + } + -class ConsumeAction { + - action : Consumer + - dest : Promise + - src : Promise + - ConsumeAction(src : Promise, dest : Promise, action : Consumer) + + run() + } + -class TransformAction { + - dest : Promise + - func : Function + - src : Promise + - TransformAction(src : Promise, dest : Promise, func : Function) + + run() + } + class App { + - DEFAULT_URL : String {static} + - executor : ExecutorService + - stopLatch : CountDownLatch + - App() + - calculateLineCount() + - calculateLowestFrequencyChar() + - characterFrequency() : Promise> + - countLines() : Promise + - download(urlString : String) : Promise + - lowestFrequencyChar() : Promise + + main(args : String[]) {static} + - promiseUsage() + - stop() + - taskCompleted() + } + class Promise { + - exceptionHandler : Consumer + - fulfillmentAction : Runnable + + Promise() + + fulfill(value : T) + + fulfillExceptionally(exception : Exception) + + fulfillInAsync(task : Callable, executor : Executor) : Promise + - handleException(exception : Exception) + + onError(exceptionHandler : Consumer) : Promise + - postFulfillment() + + thenAccept(action : Consumer) : Promise + + thenApply(func : Function) : Promise + } + class Utility { + + Utility() + + characterFrequency(fileLocation : String) : Map {static} + + countLines(fileLocation : String) : Integer {static} + + downloadFile(urlString : String) : String {static} + + lowestFrequencyChar(charFrequency : Map) : Character {static} + } +} +TransformAction --+ Promise +TransformAction --> "-src" Promise +ConsumeAction --+ Promise +ConsumeAction --> "-src" Promise +Utility --+ Map +Promise --|> PromiseSupport +@enduml \ No newline at end of file From 2d9906190221ff46ed796b6526e0ce1631623828 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:31:41 +0100 Subject: [PATCH 156/207] Issue #469: Implementation of Event-based Asynchronous pattern --- event-asynchronous/README.md | 28 +++ event-asynchronous/etc/event-asynchronous.png | Bin 0 -> 31413 bytes .../etc/event-asynchronous.ucls | 110 +++++++++++ event-asynchronous/pom.xml | 42 ++++ .../com/iluwatar/event/asynchronous/App.java | 185 ++++++++++++++++++ .../iluwatar/event/asynchronous/Event.java | 88 +++++++++ .../EventDoesNotExistException.java | 26 +++ .../event/asynchronous/EventManager.java | 148 ++++++++++++++ .../iluwatar/event/asynchronous/IEvent.java | 27 +++ .../InvalidOperationException.java | 27 +++ .../LongRunningEventException.java | 26 +++ .../MaxNumOfEventsAllowedException.java | 26 +++ .../asynchronous/ThreadCompleteListener.java | 21 ++ .../src/main/java/config.properties | 1 + .../iluwatar/event/asynchronous/AppTest.java | 32 +++ .../asynchronous/EventAsynchronousTest.java | 73 +++++++ 16 files changed, 860 insertions(+) create mode 100644 event-asynchronous/README.md create mode 100644 event-asynchronous/etc/event-asynchronous.png create mode 100644 event-asynchronous/etc/event-asynchronous.ucls create mode 100644 event-asynchronous/pom.xml create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java create mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md new file mode 100644 index 000000000000..59e6e8b335fd --- /dev/null +++ b/event-asynchronous/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Event-based Asynchronous +folder: event-asynchronous +permalink: /patterns/event-asynchronous/ +categories: Other +tags: + - Java +--- + +## Intent +The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many +of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- +(1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without interrupting your application. +(2) Execute multiple operations simultaneously, receiving notifications when each completes. +(3) Wait for resources to become available without stopping ("hanging") your application. +(4) Communicate with pending asynchronous operations using the familiar events-and-delegates model. + +![alt text](./etc/event-asynchronous.png "Event-based Asynchronous") + +## Applicability +Use the Event-based Asynchronous pattern(s) when + +* Time-consuming tasks are needed to run in the background without disrupting the current application. + +## Credits + +* [Event-based Asynchronous Pattern Overview](https://msdn.microsoft.com/en-us/library/wewwczdw%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) diff --git a/event-asynchronous/etc/event-asynchronous.png b/event-asynchronous/etc/event-asynchronous.png new file mode 100644 index 0000000000000000000000000000000000000000..a46ffe1c5d17773d38dac792349cc547adc06955 GIT binary patch literal 31413 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml new file mode 100644 index 000000000000..60ab8f0aac2c --- /dev/null +++ b/event-asynchronous/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + event-asynchronous + + + junit + junit + test + + + diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java new file mode 100644 index 000000000000..3375fd4ecbf9 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -0,0 +1,185 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Scanner; + +/** + * + * This application demonstrates the Event-based Asynchronous pattern. Essentially, users (of the pattern) may + * choose to run events in an Asynchronous or Synchronous mode. There can be multiple Asynchronous events running at + * once but only one Synchronous event can run at a time. Asynchronous events are synonymous to multi-threads. The key + * point here is that the threads run in the background and the user is free to carry on with other processes. Once an + * event is complete, the appropriate listener/callback method will be called. The listener then proceeds to carry out + * further processing depending on the needs of the user. + * + * The {@link EventManager} manages the events/threads that the user creates. Currently, the supported event operations + * are: start, stop, getStatus. For Synchronous events, the user is unable to + * start another (Synchronous) event if one is already running at the time. The running event would have to either be + * stopped or completed before a new event can be started. + * + * The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many + * of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- + * (1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without + * interrupting your application. (2) Execute multiple operations simultaneously, receiving notifications when each + * completes. (3) Wait for resources to become available without stopping ("hanging") your application. (4) Communicate + * with pending asynchronous operations using the familiar events-and-delegates model. + * + * @see EventManager + * @see Event + * + */ +public class App { + + boolean interactiveMode = false; + + public static void main(String[] args) { + App app = new App(); + + app.setUp(); + app.run(); + } + + /** + * App can run in interactive mode or not. Interactive mode == Allow user interaction with command line. + * Non-interactive is a quick sequential run through the available {@link EventManager} operations. + */ + public void setUp() { + Properties prop = new Properties(); + String propFileName = "config.properties"; + + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + + if (inputStream != null) { + try { + prop.load(inputStream); + } catch (IOException e) { + } + String property = prop.getProperty("INTERACTIVE_MODE"); + if (property.equalsIgnoreCase("YES")) { + interactiveMode = true; + } + } + } + + public void run() { + if (interactiveMode) { + runInteractiveMode(); + } else { + quickRun(); + } + } + + public void quickRun() { + EventManager eventManager = new EventManager(); + + try { + // Create an Asynchronous event. + int aEventID = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventID + "] has been created."); + eventManager.startEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been started."); + + // Create a Synchronous event. + int sEventID = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventID + "] has been created."); + eventManager.startEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been started."); + + eventManager.getStatus(aEventID); + eventManager.getStatus(sEventID); + + eventManager.stopEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been stopped."); + eventManager.stopEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been stopped."); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + public void runInteractiveMode() { + EventManager eventManager = new EventManager(); + + Scanner s = new Scanner(System.in); + int option = 0; + option = -1; + while (option != 5) { + System.out + .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + System.out.print("Choose [1,2,3,4,5]: "); + option = s.nextInt(); + + if (option == 1) { + s.nextLine(); + System.out.print("(A)sync or (S)ync event?: "); + String eventType = s.nextLine(); + System.out.print("How long should this event run for (in seconds)?: "); + int eventTime = s.nextInt(); + if (eventType.equalsIgnoreCase("A")) { + try { + int eventID = eventManager.createAsyncEvent(eventTime); + System.out.println("Event [" + eventID + "] has been created."); + eventManager.startEvent(eventID); + System.out.println("Event [" + eventID + "] has been started."); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (eventType.equalsIgnoreCase("S")) { + try { + int eventID = eventManager.createSyncEvent(eventTime); + System.out.println("Event [" + eventID + "] has been created."); + eventManager.startEvent(eventID); + System.out.println("Event [" + eventID + "] has been started."); + } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException + | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else { + System.out.println("Unknown event type."); + } + } else if (option == 2) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.stopEvent(eventID); + System.out.println("Event [" + eventID + "] has been stopped."); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 3) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.getStatus(eventID); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 4) { + eventManager.getStatusOfAllEvents(); + } + } + + s.close(); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java new file mode 100644 index 000000000000..48dc37236896 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -0,0 +1,88 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +/** + * + * Each Event runs as a separate/individual thread. + * + */ +public class Event implements IEvent, Runnable { + + private int eventID; + private int eventTime; + private Thread thread; + private long counter = 0; + private boolean isComplete = false; + private ThreadCompleteListener eventListener; + + public Event(int eventID, int eventTime) { + this.eventID = eventID; + this.eventTime = eventTime; + } + + @Override + public void start() { + thread = new Thread(this); + thread.start(); + } + + @Override + public void stop() { + thread.interrupt(); + } + + @Override + public void status() { + if (!isComplete) { + System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + } else { + System.out.println("[" + eventID + "] I am done."); + } + } + + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime * 1000); + while (System.currentTimeMillis() < endTime) { + try { + counter += 1; + Thread.sleep(5000); // Sleep for 5 seconds. + } catch (InterruptedException e) { + return; + } + } + isComplete = true; + notifyListener(); + } + + public final void addListener(final ThreadCompleteListener listener) { + this.eventListener = listener; + } + + public final void removeListener(final ThreadCompleteListener listener) { + this.eventListener = null; + } + + private final void notifyListener() { + if (eventListener != null) { + eventListener.notifyOfThreadComplete(eventID); + } + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java new file mode 100644 index 000000000000..77c1d479bfcb --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +public class EventDoesNotExistException extends Exception { + + private static final long serialVersionUID = -3398463738273811509L; + + public EventDoesNotExistException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java new file mode 100644 index 000000000000..305548111d0d --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -0,0 +1,148 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + * EventManager handles and maintains a pool of event threads. {@link Event} threads are created upon user request. Thre + * are two types of events; Asynchronous and Synchronous. There can be multiple Asynchronous events running at once but + * only one Synchronous event running at a time. Currently supported event operations are: start, stop, and getStatus. + * Once an event is complete, it then notifies EventManager through a listener. The EventManager then takes the event + * out of the pool. + * + */ +public class EventManager implements ThreadCompleteListener { + + private int minID = 1; + private int maxID = Integer.MAX_VALUE - 1; // Be cautious of overflows. + private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) + private int maxEventTime = 1800; // in seconds / 30 minutes. + private int currentlyRunningSyncEvent = -1; + private Random rand; + private Map eventPool; + + public EventManager() { + rand = new Random(1); + eventPool = new ConcurrentHashMap(maxRunningEvents); + + } + + // Create a Synchronous event. + public int createSyncEvent(int eventTime) + throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { + int eventID = createEvent(eventTime); + if (currentlyRunningSyncEvent != -1) { + throw new InvalidOperationException( + "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); + } + currentlyRunningSyncEvent = eventID; + + return eventID; + } + + // Create an Asynchronous event. + public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + return createEvent(eventTime); + } + + private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + if (eventPool.size() == maxRunningEvents) { + throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); + } + + if (eventTime >= maxEventTime) { + throw new LongRunningEventException( + "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + } + + int newEventID = generateID(); + + Event newEvent = new Event(newEventID, eventTime); + newEvent.addListener(this); + eventPool.put(newEventID, newEvent); + + return newEventID; + } + + public void startEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).start(); + } + + public void stopEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + if (eventID == currentlyRunningSyncEvent) { + currentlyRunningSyncEvent = -1; + } + + eventPool.get(eventID).stop(); + eventPool.remove(eventID); + } + + public void getStatus(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).status(); + } + + @SuppressWarnings("rawtypes") + public void getStatusOfAllEvents() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).status(); + } + } + + /** + * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most + * Integer.MAX_VALUE - 1. + */ + private int generateID() { + // nextInt is normally exclusive of the top value, + // so add 1 to make it inclusive + int randomNum = rand.nextInt((maxID - minID) + 1) + minID; + while (eventPool.containsKey(randomNum)) { + randomNum = rand.nextInt((maxID - minID) + 1) + minID; + } + + return randomNum; + } + + /** + * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. + */ + @Override + public void notifyOfThreadComplete(int eventID) { + eventPool.get(eventID).status(); + eventPool.remove(eventID); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java new file mode 100644 index 000000000000..448c02e84862 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -0,0 +1,27 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +public interface IEvent { + + public void start(); + + public void stop(); + + public void status(); + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java new file mode 100644 index 000000000000..4fd5b0eed632 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java @@ -0,0 +1,27 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +public class InvalidOperationException extends Exception { + + private static final long serialVersionUID = -6191545255213410803L; + + public InvalidOperationException(String message) { + super(message); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java new file mode 100644 index 000000000000..6817b1dd8c1b --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +public class LongRunningEventException extends Exception { + + private static final long serialVersionUID = -483423544320148809L; + + public LongRunningEventException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java new file mode 100644 index 000000000000..9f8f2891c14e --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +public class MaxNumOfEventsAllowedException extends Exception { + + private static final long serialVersionUID = -8430876973516292695L; + + public MaxNumOfEventsAllowedException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java new file mode 100644 index 000000000000..e5c910289fd7 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -0,0 +1,21 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +public interface ThreadCompleteListener { + void notifyOfThreadComplete(final int eventID); +} diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties new file mode 100644 index 000000000000..edbe90e0544e --- /dev/null +++ b/event-asynchronous/src/main/java/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java new file mode 100644 index 000000000000..8736fcf777e0 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java @@ -0,0 +1,32 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +import java.io.IOException; + +import org.junit.Test; + +/** + * Tests that EventAsynchronous example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java new file mode 100644 index 000000000000..0ab901106796 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License Copyright (c) 2014 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.event.asynchronous; + +import org.junit.Before; +import org.junit.Test; + +/** + * + * Application test + * + */ +public class EventAsynchronousTest { + App app; + + @Before + public void setUp() { + app = new App(); + } + + @Test + public void testAsynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int aEventID = eventManager.createAsyncEvent(60); + eventManager.startEvent(aEventID); + eventManager.stopEvent(aEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + eventManager.stopEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testUnsuccessfulSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } +} From e1836fee2f3fe7944faf89ff1b66b137294492ed Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:44:39 +0100 Subject: [PATCH 157/207] Updated parent POM to include new pattern (Event-asynchronous) --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 30234699313d..058dee5a1b4a 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,7 @@ aggregator-microservices promise page-object + event-asynchronous From f11597136f8bebdcb208a141f8e0fd47ee4dbd1e Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 00:32:05 +0100 Subject: [PATCH 158/207] Fixed Checkstyle errors. --- .../com/iluwatar/event/asynchronous/App.java | 69 +++++++----- .../iluwatar/event/asynchronous/Event.java | 12 +-- .../event/asynchronous/EventManager.java | 102 ++++++++++++------ .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 20 ++-- 5 files changed, 130 insertions(+), 75 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 3375fd4ecbf9..fa6116b46488 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -50,6 +50,11 @@ public class App { boolean interactiveMode = false; + /** + * Program entry point. + * + * @param args command line args + */ public static void main(String[] args) { App app = new App(); @@ -71,6 +76,7 @@ public void setUp() { try { prop.load(inputStream); } catch (IOException e) { + System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -79,6 +85,9 @@ public void setUp() { } } + /** + * Run program in either interactive mode or not. + */ public void run() { if (interactiveMode) { runInteractiveMode(); @@ -87,29 +96,32 @@ public void run() { } } + /** + * Run program in non-interactive mode. + */ public void quickRun() { EventManager eventManager = new EventManager(); try { // Create an Asynchronous event. - int aEventID = eventManager.createAsyncEvent(60); - System.out.println("Event [" + aEventID + "] has been created."); - eventManager.startEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been started."); + int aEventId = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventId + "] has been created."); + eventManager.startEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventID = eventManager.createSyncEvent(60); - System.out.println("Event [" + sEventID + "] has been created."); - eventManager.startEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been started."); + int sEventId = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventId + "] has been created."); + eventManager.startEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventID); - eventManager.getStatus(sEventID); + eventManager.getStatus(aEventId); + eventManager.getStatus(sEventId); - eventManager.stopEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been stopped."); - eventManager.stopEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been stopped."); + eventManager.stopEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been stopped."); + eventManager.stopEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { @@ -117,6 +129,9 @@ public void quickRun() { } } + /** + * Run program in interactive mode. + */ public void runInteractiveMode() { EventManager eventManager = new EventManager(); @@ -137,19 +152,19 @@ public void runInteractiveMode() { int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventID = eventManager.createAsyncEvent(eventTime); - System.out.println("Event [" + eventID + "] has been created."); - eventManager.startEvent(eventID); - System.out.println("Event [" + eventID + "] has been started."); + int eventId = eventManager.createAsyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventID = eventManager.createSyncEvent(eventTime); - System.out.println("Event [" + eventID + "] has been created."); - eventManager.startEvent(eventID); - System.out.println("Event [" + eventID + "] has been started."); + int eventId = eventManager.createSyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -159,18 +174,18 @@ public void runInteractiveMode() { } } else if (option == 2) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.stopEvent(eventID); - System.out.println("Event [" + eventID + "] has been stopped."); + eventManager.stopEvent(eventId); + System.out.println("Event [" + eventId + "] has been stopped."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.getStatus(eventID); + eventManager.getStatus(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 48dc37236896..4b4fe1d94412 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -23,15 +23,15 @@ */ public class Event implements IEvent, Runnable { - private int eventID; + private int eventId; private int eventTime; private Thread thread; private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventID, int eventTime) { - this.eventID = eventID; + public Event(int eventId, int eventTime) { + this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +49,9 @@ public void stop() { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); } else { - System.out.println("[" + eventID + "] I am done."); + System.out.println("[" + eventId + "] I am done."); } } @@ -81,7 +81,7 @@ public final void removeListener(final ThreadCompleteListener listener) { private final void notifyListener() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventID); + eventListener.notifyOfThreadComplete(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index 305548111d0d..d3278594fd35 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,34 +32,53 @@ */ public class EventManager implements ThreadCompleteListener { - private int minID = 1; - private int maxID = Integer.MAX_VALUE - 1; // Be cautious of overflows. + private int minId = 1; + private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) private int maxEventTime = 1800; // in seconds / 30 minutes. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; + /** + * EventManager constructor. + * + */ public EventManager() { rand = new Random(1); eventPool = new ConcurrentHashMap(maxRunningEvents); } - // Create a Synchronous event. + /** + * Create a Synchronous event. + * + * @param eventTime Time an event should run for. + * @return eventId + * @throws MaxNumOfEventsAllowedException When too many events are running at a time. + * @throws InvalidOperationException No new synchronous events can be created when one is already running. + * @throws LongRunningEventException Long running events are not allowed in the app. + */ public int createSyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventID = createEvent(eventTime); + int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } - currentlyRunningSyncEvent = eventID; + currentlyRunningSyncEvent = eventId; - return eventID; + return eventId; } - // Create an Asynchronous event. + /** + * Create an Asynchronous event. + * + * @param eventTime Time an event should run for. + * @return eventId + * @throws MaxNumOfEventsAllowedException When too many events are running at a time. + * @throws LongRunningEventException Long running events are not allowed in the app. + */ public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } @@ -74,44 +93,65 @@ private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, Lo "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); } - int newEventID = generateID(); + int newEventId = generateId(); - Event newEvent = new Event(newEventID, eventTime); + Event newEvent = new Event(newEventId, eventTime); newEvent.addListener(this); - eventPool.put(newEventID, newEvent); + eventPool.put(newEventId, newEvent); - return newEventID; + return newEventId; } - public void startEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Starts event. + * + * @param eventId The event that needs to be started. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void startEvent(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).start(); + eventPool.get(eventId).start(); } - public void stopEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Stops event. + * + * @param eventId The event that needs to be stopped. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void stopEvent(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - if (eventID == currentlyRunningSyncEvent) { + if (eventId == currentlyRunningSyncEvent) { currentlyRunningSyncEvent = -1; } - eventPool.get(eventID).stop(); - eventPool.remove(eventID); + eventPool.get(eventId).stop(); + eventPool.remove(eventId); } - public void getStatus(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Get status of a running event. + * + * @param eventId The event to inquire status of. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void getStatus(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).status(); + eventPool.get(eventId).status(); } + /** + * Gets status of all running events. + */ @SuppressWarnings("rawtypes") public void getStatusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); @@ -125,12 +165,12 @@ public void getStatusOfAllEvents() { * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most * Integer.MAX_VALUE - 1. */ - private int generateID() { + private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxID - minID) + 1) + minID; + int randomNum = rand.nextInt((maxId - minId) + 1) + minId; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxID - minID) + 1) + minID; + randomNum = rand.nextInt((maxId - minId) + 1) + minId; } return randomNum; @@ -140,9 +180,9 @@ private int generateID() { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventID) { - eventPool.get(eventID).status(); - eventPool.remove(eventID); + public void notifyOfThreadComplete(int eventId) { + eventPool.get(eventId).status(); + eventPool.remove(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index e5c910289fd7..88f300634915 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventID); + void notifyOfThreadComplete(final int eventId); } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 0ab901106796..392c7fba6ccd 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -36,9 +36,9 @@ public void setUp() { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventID = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventID); - eventManager.stopEvent(aEventID); + int aEventId = eventManager.createAsyncEvent(60); + eventManager.startEvent(aEventId); + eventManager.stopEvent(aEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,9 +48,9 @@ public void testAsynchronousEvent() { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - eventManager.stopEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + eventManager.stopEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); @@ -61,10 +61,10 @@ public void testSynchronousEvent() { public void testUnsuccessfulSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); From 233f1e69f9401611967c8be6cc70f16d2ac18e1b Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 22:00:19 +0100 Subject: [PATCH 159/207] Removed PUBLIC modifiers from IEvent --- .../main/java/com/iluwatar/event/asynchronous/IEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java index 448c02e84862..bcd78b6c05e6 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -18,10 +18,10 @@ public interface IEvent { - public void start(); + void start(); - public void stop(); + void stop(); - public void status(); + void status(); } From ab68129829b227cf6ac5637538775219a8463ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 3 Sep 2016 22:02:08 +0300 Subject: [PATCH 160/207] Hexagonal pattern: move business logic to core --- .../administration/LotteryAdministration.java | 4 +- .../LotteryAdministrationImpl.java | 47 ++------ .../hexagonal/domain/LotterySystem.java | 58 ++++++++++ .../hexagonal/domain/LotterySystemImpl.java | 107 ++++++++++++++++++ .../hexagonal/service/LotteryService.java | 4 +- .../hexagonal/service/LotteryServiceImpl.java | 45 ++------ .../{lottery => domain}/LotteryTest.java | 38 +++---- 7 files changed, 202 insertions(+), 101 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/{lottery => domain}/LotteryTest.java (67%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index bc625b2301cc..c6c034ac9235 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -22,12 +22,12 @@ */ package com.iluwatar.hexagonal.administration; -import java.util.Map; - import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; +import java.util.Map; + /** * * Administrator interface for lottery service. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index a452600aa1de..2003849c277e 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -22,22 +22,13 @@ */ package com.iluwatar.hexagonal.administration; -import java.util.Map; - -import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; -import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; -import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +import java.util.Map; /** * @@ -46,42 +37,24 @@ */ public class LotteryAdministrationImpl implements LotteryAdministration { - private final LotteryTicketRepository repository; - private final LotteryService service = new LotteryServiceImpl(); - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - private final WireTransfers bank = new WireTransfersImpl(); + private final LotterySystem lotterySystem; + public LotteryAdministrationImpl() { - repository = new LotteryTicketInMemoryRepository(); + lotterySystem = new LotterySystemImpl(); } @Override public Map getAllSubmittedTickets() { - return repository.findAll(); + return lotterySystem.getAllSubmittedTickets(); } @Override public LotteryNumbers performLottery() { - LotteryNumbers numbers = LotteryNumbers.createRandom(); - Map tickets = getAllSubmittedTickets(); - for (LotteryTicketId id: tickets.keySet()) { - LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); - if (result.getResult().equals(CheckResult.WIN_PRIZE)) { - boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, - tickets.get(id).getPlayerDetails().getBankAccount()); - if (transferred) { - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } else { - notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } - } else if (result.getResult().equals(CheckResult.NO_PRIZE)) { - notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); - } - } - return numbers; + return lotterySystem.performLottery(); } @Override public void resetLottery() { - repository.deleteAll(); + lotterySystem.resetLottery(); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java new file mode 100644 index 000000000000..2ee114556f00 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.Map; +import java.util.Optional; + +/** + * Lottery system interface + */ +public interface LotterySystem { + + /** + * Get all the lottery tickets submitted for lottery + */ + Map getAllSubmittedTickets(); + + /** + * Draw lottery numbers + */ + LotteryNumbers performLottery(); + + /** + * Begin new lottery round + */ + void resetLottery(); + + /** + * Submit lottery ticket to participate in the lottery + */ + Optional submitTicket(LotteryTicket ticket); + + /** + * Check if lottery ticket has won + */ + LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java new file mode 100644 index 000000000000..a9290520cf8b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java @@ -0,0 +1,107 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; + +import java.util.Map; +import java.util.Optional; + +/** + * Lottery system implementation + */ +public class LotterySystemImpl implements LotterySystem { + + private final LotteryTicketRepository repository; + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + private final WireTransfers bank = new WireTransfersImpl(); + + public LotterySystemImpl() { + repository = new LotteryTicketInMemoryRepository(); + } + + @Override + public Map getAllSubmittedTickets() { + return repository.findAll(); + } + + @Override + public LotteryNumbers performLottery() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id : tickets.keySet()) { + LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, + tickets.get(id).getPlayerDetails().getBankAccount()); + if (transferred) { + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } else { + notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; + } + + @Override + public void resetLottery() { + repository.deleteAll(); + } + + @Override + public Optional submitTicket(LotteryTicket ticket) { + boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), + LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; + } + + @Override + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index 0056e794b0d5..ef2202968056 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -22,13 +22,13 @@ */ package com.iluwatar.hexagonal.service; -import java.util.Optional; - import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; +import java.util.Optional; + /** * * Interface for submitting and checking lottery tickets. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 58df1c7c8d39..5bce62054e4a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -22,20 +22,14 @@ */ package com.iluwatar.hexagonal.service; -import java.util.Optional; - -import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; -import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; + +import java.util.Optional; /** * @@ -44,45 +38,22 @@ */ public class LotteryServiceImpl implements LotteryService { - private final LotteryTicketRepository repository; - - private final WireTransfers bank = new WireTransfersImpl(); - - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + private final LotterySystem lotterySystem; /** * Constructor */ public LotteryServiceImpl() { - repository = new LotteryTicketInMemoryRepository(); + lotterySystem = new LotterySystemImpl(); } @Override public Optional submitTicket(LotteryTicket ticket) { - boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), - LotteryConstants.SERVICE_BANK_ACCOUNT); - if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); - return Optional.empty(); - } - Optional optional = repository.save(ticket); - if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); - } - return optional; + return lotterySystem.submitTicket(ticket); } @Override public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - Optional optional = repository.findById(id); - if (optional.isPresent()) { - if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000); - } else { - return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); - } - } else { - return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED); - } + return lotterySystem.checkTicketForPrize(id, winningNumbers); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java similarity index 67% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 27e5bb6e471d..59c8c19307b7 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.lottery; +package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -30,22 +30,15 @@ import java.util.Map; import java.util.Optional; +import com.iluwatar.hexagonal.domain.*; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; import com.iluwatar.hexagonal.test.LotteryTestUtils; /** @@ -55,8 +48,7 @@ */ public class LotteryTest { - private final LotteryAdministration admin = new LotteryAdministrationImpl(); - private final LotteryService service = new LotteryServiceImpl(); + private final LotterySystem lotterySystem = new LotterySystemImpl(); private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); private final WireTransfers wireTransfers = new WireTransfersImpl(); @@ -72,34 +64,34 @@ public void testLottery() { wireTransfers.setFunds("123-12312", 100); // admin resets the lottery - admin.resetLottery(); - assertEquals(admin.getAllSubmittedTickets().size(), 0); + lotterySystem.resetLottery(); + assertEquals(lotterySystem.getAllSubmittedTickets().size(), 0); // players submit the lottery tickets - Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + Optional ticket1 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); assertTrue(ticket1.isPresent()); - Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + Optional ticket2 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); assertTrue(ticket2.isPresent()); - Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + Optional ticket3 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); assertTrue(ticket3.isPresent()); - assertEquals(admin.getAllSubmittedTickets().size(), 3); + assertEquals(lotterySystem.getAllSubmittedTickets().size(), 3); // perform lottery - LotteryNumbers winningNumbers = admin.performLottery(); + LotteryNumbers winningNumbers = lotterySystem.performLottery(); // cheat a bit for testing sake, use winning numbers to submit another ticket - Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + Optional ticket4 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", "123-12312", "+12421255", winningNumbers.getNumbers())); assertTrue(ticket4.isPresent()); - assertEquals(admin.getAllSubmittedTickets().size(), 4); + assertEquals(lotterySystem.getAllSubmittedTickets().size(), 4); // check winners - Map tickets = admin.getAllSubmittedTickets(); + Map tickets = lotterySystem.getAllSubmittedTickets(); for (LotteryTicketId id: tickets.keySet()) { - LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); + LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(id, winningNumbers); assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { assertTrue(checkResult.getPrizeAmount() > 0); @@ -109,7 +101,7 @@ public void testLottery() { } // check another ticket that has not been submitted - LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(new LotteryTicketId(), winningNumbers); assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); assertEquals(checkResult.getPrizeAmount(), 0); } From 3cb872807eee1173ef4724f51aab10a12e963dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 4 Sep 2016 10:33:26 +0300 Subject: [PATCH 161/207] Hexagonal pattern: remove unnecessary repository usage from a unit test --- .../java/com/iluwatar/hexagonal/domain/LotteryTest.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 59c8c19307b7..3e114ddc44ea 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -49,20 +49,16 @@ public class LotteryTest { private final LotterySystem lotterySystem = new LotterySystemImpl(); - private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); private final WireTransfers wireTransfers = new WireTransfersImpl(); @Before public void clear() { - repository.deleteAll(); + // add funds to the test player's bank account + wireTransfers.setFunds("123-12312", 100); } @Test public void testLottery() { - - // setup bank account with funds - wireTransfers.setFunds("123-12312", 100); - // admin resets the lottery lotterySystem.resetLottery(); assertEquals(lotterySystem.getAllSubmittedTickets().size(), 0); From 4493341ba6e122c605b05e0d1249631b8bada248 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Sun, 4 Sep 2016 11:00:24 +0200 Subject: [PATCH 162/207] add documentation to 'use latest java 8' change --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 19d4614f4bd6..613f737d7164 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh +# use latest java version available instead of travis default addons: apt: packages: From 22821ba8cc677172e802315a4c85592123286d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 6 Sep 2016 21:35:36 +0300 Subject: [PATCH 163/207] Move Guice to parent pom dependency management section --- dependency-injection/pom.xml | 9 +- pom.xml | 815 ++++++++++++++++++----------------- 2 files changed, 414 insertions(+), 410 deletions(-) diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index 3472da2400ae..88ccdd2d4e2c 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -43,10 +43,9 @@ mockito-core test - - com.google.inject - guice - 4.0 - + + com.google.inject + guice + diff --git a/pom.xml b/pom.xml index 30234699313d..023579e9722b 100644 --- a/pom.xml +++ b/pom.xml @@ -17,413 +17,418 @@ 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. ---> - 4.0.0 - - com.iluwatar - java-design-patterns - 1.13.0-SNAPSHOT - pom - - 2014 - - - UTF-8 - 5.0.1.Final - 4.2.4.RELEASE - 1.3.3.RELEASE - 1.9.2.RELEASE - 1.4.190 - 4.12 - 3.0 - 4.0.0 - 0.7.2.201409121644 - 1.4 - 2.16.1 - 1.2.17 - 19.0 - 1.15.1 - 1.10.19 - 4.12.1 +--> + + 4.0.0 + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + pom + 2014 + + UTF-8 + 5.0.1.Final + 4.2.4.RELEASE + 1.3.3.RELEASE + 1.9.2.RELEASE + 1.4.190 + 4.12 + 3.0 + 4.0.0 + 0.7.2.201409121644 + 1.4 + 2.16.1 + 1.2.17 + 19.0 + 1.15.1 + 1.10.19 + 4.12.1 4.5.2 2.22 1.4.1 - - - abstract-factory - builder - factory-method - prototype - singleton - adapter - bridge - composite - dao - data-mapper - decorator - facade - flyweight - proxy - chain - command - interpreter - iterator - mediator - memento - model-view-presenter - observer - state - strategy - template-method - visitor - double-checked-locking - servant - service-locator - null-object - event-aggregator - callback - execute-around - property - intercepting-filter - producer-consumer - poison-pill - reader-writer-lock - lazy-loading - service-layer - specification - tolerant-reader - model-view-controller - flux - double-dispatch - multiton - resource-acquisition-is-initialization - thread-pool - twin - private-class-data - object-pool - dependency-injection - naked-objects - front-controller - repository - async-method-invocation - monostate - step-builder - business-delegate - half-sync-half-async - layers - message-channel - fluentinterface - reactor - caching - publish-subscribe - delegation - event-driven-architecture - api-gateway - factory-kit - feature-toggle - value-object - monad - mute-idiom - mutex - semaphore - hexagonal - abstract-document - aggregator-microservices - promise + 4.0 + + + abstract-factory + builder + factory-method + prototype + singleton + adapter + bridge + composite + dao + data-mapper + decorator + facade + flyweight + proxy + chain + command + interpreter + iterator + mediator + memento + model-view-presenter + observer + state + strategy + template-method + visitor + double-checked-locking + servant + service-locator + null-object + event-aggregator + callback + execute-around + property + intercepting-filter + producer-consumer + poison-pill + reader-writer-lock + lazy-loading + service-layer + specification + tolerant-reader + model-view-controller + flux + double-dispatch + multiton + resource-acquisition-is-initialization + thread-pool + twin + private-class-data + object-pool + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + monostate + step-builder + business-delegate + half-sync-half-async + layers + message-channel + fluentinterface + reactor + caching + publish-subscribe + delegation + event-driven-architecture + api-gateway + factory-kit + feature-toggle + value-object + monad + mute-idiom + mutex + semaphore + hexagonal + abstract-document + aggregator-microservices + promise page-object - + - - - - org.hibernate - hibernate-core - ${hibernate.version} - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.springframework - spring-test - ${spring.version} - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - org.springframework.data - spring-data-jpa - ${spring-data.version} - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.apache.httpcomponents - httpclient - ${apache-httpcomponents.version} - - - com.h2database - h2 - ${h2.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.apache.camel - camel-core - ${camel.version} - - - org.apache.camel - camel-stream - ${camel.version} - - - junit - junit - ${junit.version} - test - - - org.mockito - mockito-core - ${mockito.version} - test - - - log4j - log4j - ${log4j.version} - - - com.google.guava - guava - ${guava.version} - - - com.github.stefanbirkner - system-rules - ${systemrules.version} - test - - - de.bechte.junit - junit-hierarchicalcontextrunner - ${hierarchical-junit-runner-version} - test - - - net.sourceforge.htmlunit - htmlunit - ${htmlunit.version} - test - - - + + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + org.springframework + spring-test + ${spring.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.apache.httpcomponents + httpclient + ${apache-httpcomponents.version} + + + com.h2database + h2 + ${h2.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + log4j + log4j + ${log4j.version} + + + com.google.guava + guava + ${guava.version} + + + com.github.stefanbirkner + system-rules + ${systemrules.version} + test + + + de.bechte.junit + junit-hierarchicalcontextrunner + ${hierarchical-junit-runner-version} + test + + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + + + com.google.inject + guice + ${guice.version} + + + - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - - jacoco-maven-plugin - - - [0.6.2,) - - - prepare-agent - - - - - - - - - - - - + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + + jacoco-maven-plugin + + + [0.6.2,) + + + prepare-agent + + + + + + + + + + + + - - - - org.apache.maven.plugins - maven-compiler-plugin - ${compiler.version} - - 1.8 - 1.8 - - - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - **com.steadystate* - - - - - prepare-agent - - prepare-agent - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.version} + + 1.8 + 1.8 + + + + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls.version} + + jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + + domainapp/dom/modules/simple/QSimpleObject.class + **com.steadystate* + + + + + prepare-agent + + prepare-agent + + + + - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - - - validate - - check - - validate - - checkstyle.xml - checkstyle-suppressions.xml - UTF-8 - true - true - true - - - - + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + validate + + check + + validate + + checkstyle.xml + checkstyle-suppressions.xml + UTF-8 + true + true + true + + + + - - org.jacoco - jacoco-maven-plugin - 0.7.5.201505241946 - - - - prepare-agent - - - - report - prepare-package - - report - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18.1 - - - org.apache.maven.surefire - surefire-junit47 - 2.18.1 - - - - -Xmx1024M ${argLine} - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - true - 5 - true - - - - - check - - - exclude-pmd.properties - - - + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.surefire + surefire-junit47 + 2.18.1 + + + + -Xmx1024M ${argLine} + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + true + 5 + true + + + + + check + + + exclude-pmd.properties + + + - - com.mycila - license-maven-plugin - 2.11 - -
    com/mycila/maven/plugin/license/templates/MIT.txt
    - - Ilkka Seppälä - - true -
    - - - install-format - install - - format - - - -
    + + com.mycila + license-maven-plugin + 2.11 + +
    com/mycila/maven/plugin/license/templates/MIT.txt
    + + Ilkka Seppälä + + true +
    + + + install-format + install + + format + + + +
    com.github.markusmo3.urm @@ -448,17 +453,17 @@ -
    -
    + +
    - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - - + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + +
    From 348e577e8ed80b3cf7642b40d5b5378e6965fea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 6 Sep 2016 21:39:08 +0300 Subject: [PATCH 164/207] Hexagonal pattern: Add Guice dependency --- hexagonal/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index e9e2a502d54c..a73fea48d748 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -39,5 +39,9 @@ junit test + + com.google.inject + guice + From 1b10ddbb73ff167e6a411b73442cfeb4b8ed754d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 6 Sep 2016 22:39:39 +0300 Subject: [PATCH 165/207] Hexagonal pattern: Use Guice dependency injection --- .../main/java/com/iluwatar/hexagonal/App.java | 11 ++-- .../com/iluwatar/hexagonal/LotteryModule.java | 52 +++++++++++++++++++ .../LotteryAdministrationImpl.java | 7 +-- .../hexagonal/domain/LotterySystemImpl.java | 27 ++++++---- .../hexagonal/service/LotteryServiceImpl.java | 7 +-- .../hexagonal/LotteryTestingModule.java | 52 +++++++++++++++++++ .../hexagonal/domain/LotteryTest.java | 21 ++++++-- 7 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 92ebf3572d81..8f99fb15a34b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -26,15 +26,15 @@ import java.util.List; import java.util.Random; +import com.google.inject.Guice; +import com.google.inject.Injector; import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; /** * @@ -124,12 +124,15 @@ public class App { * Program entry point */ public static void main(String[] args) { + + Injector injector = Guice.createInjector(new LotteryModule()); + // start new lottery round - LotteryAdministration administartion = new LotteryAdministrationImpl(); + LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); administartion.resetLottery(); // submit some lottery tickets - LotteryServiceImpl service = new LotteryServiceImpl(); + LotteryService service = injector.getInstance(LotteryService.class); submitTickets(service, 20); // perform lottery diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java new file mode 100644 index 000000000000..b51bff9f4c18 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal; + +import com.google.inject.AbstractModule; +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * Guice module for binding production dependencies + */ +public class LotteryModule extends AbstractModule { + @Override + protected void configure() { + bind(LotteryTicketRepository.class).to(LotteryTicketInMemoryRepository.class); + bind(LotterySystem.class).to(LotterySystemImpl.class); + bind(LotteryNotifications.class).to(LotteryNotificationsImpl.class); + bind(WireTransfers.class).to(WireTransfersImpl.class); + bind(LotteryAdministration.class).to(LotteryAdministrationImpl.class); + bind(LotteryService.class).to(LotteryServiceImpl.class); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 2003849c277e..bef2f07c3c90 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -22,9 +22,9 @@ */ package com.iluwatar.hexagonal.administration; +import com.google.inject.Inject; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; @@ -39,8 +39,9 @@ public class LotteryAdministrationImpl implements LotteryAdministration { private final LotterySystem lotterySystem; - public LotteryAdministrationImpl() { - lotterySystem = new LotterySystemImpl(); + @Inject + public LotteryAdministrationImpl(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java index a9290520cf8b..e37185143420 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java @@ -22,12 +22,10 @@ */ package com.iluwatar.hexagonal.domain; +import com.google.inject.Inject; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; import java.util.Map; import java.util.Optional; @@ -38,11 +36,18 @@ public class LotterySystemImpl implements LotterySystem { private final LotteryTicketRepository repository; - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - private final WireTransfers bank = new WireTransfersImpl(); + private final LotteryNotifications notifications; + private final WireTransfers wireTransfers; - public LotterySystemImpl() { - repository = new LotteryTicketInMemoryRepository(); + /** + * Constructor + */ + @Inject + public LotterySystemImpl(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { + this.repository = repository; + this.notifications = notifications; + this.wireTransfers = wireTransfers; } @Override @@ -57,8 +62,8 @@ public LotteryNumbers performLottery() { for (LotteryTicketId id : tickets.keySet()) { LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, - tickets.get(id).getPlayerDetails().getBankAccount()); + boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, + LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); if (transferred) { notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); } else { @@ -78,8 +83,8 @@ public void resetLottery() { @Override public Optional submitTicket(LotteryTicket ticket) { - boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), - LotteryConstants.SERVICE_BANK_ACCOUNT); + boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, + ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); if (result == false) { notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); return Optional.empty(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 5bce62054e4a..c912bb0b4d1f 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -22,9 +22,9 @@ */ package com.iluwatar.hexagonal.service; +import com.google.inject.Inject; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; @@ -43,8 +43,9 @@ public class LotteryServiceImpl implements LotteryService { /** * Constructor */ - public LotteryServiceImpl() { - lotterySystem = new LotterySystemImpl(); + @Inject + public LotteryServiceImpl(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; } @Override diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java new file mode 100644 index 000000000000..5fbeb8240471 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal; + +import com.google.inject.AbstractModule; +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * Guice module for testing dependencies + */ +public class LotteryTestingModule extends AbstractModule { + @Override + protected void configure() { + bind(LotteryTicketRepository.class).to(LotteryTicketInMemoryRepository.class); + bind(LotterySystem.class).to(LotterySystemImpl.class); + bind(LotteryNotifications.class).to(LotteryNotificationsImpl.class); + bind(WireTransfers.class).to(WireTransfersImpl.class); + bind(LotteryAdministration.class).to(LotteryAdministrationImpl.class); + bind(LotteryService.class).to(LotteryServiceImpl.class); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 3e114ddc44ea..331cc0d66697 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -30,6 +30,11 @@ import java.util.Map; import java.util.Optional; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.iluwatar.hexagonal.LotteryModule; +import com.iluwatar.hexagonal.LotteryTestingModule; import com.iluwatar.hexagonal.domain.*; import org.junit.Before; import org.junit.Test; @@ -48,11 +53,19 @@ */ public class LotteryTest { - private final LotterySystem lotterySystem = new LotterySystemImpl(); - private final WireTransfers wireTransfers = new WireTransfersImpl(); - + private Injector injector; + @Inject + private LotterySystem lotterySystem; + @Inject + private WireTransfers wireTransfers; + + public LotteryTest() { + this.injector = Guice.createInjector(new LotteryTestingModule()); + } + @Before - public void clear() { + public void setup() { + injector.injectMembers(this); // add funds to the test player's bank account wireTransfers.setFunds("123-12312", 100); } From 0f2807b9cfd2863d6486e968bd32fca2d99c42d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 9 Sep 2016 21:36:17 +0300 Subject: [PATCH 166/207] Hexagonal pattern: More descriptive class names --- hexagonal/etc/hexagonal.png | Bin 165940 -> 148602 bytes hexagonal/etc/hexagonal.ucls | 297 ++++++++---------- .../main/java/com/iluwatar/hexagonal/App.java | 4 +- .../com/iluwatar/hexagonal/LotteryModule.java | 20 +- ...onImpl.java => ConsoleAdministration.java} | 4 +- ...reTransfersImpl.java => InMemoryBank.java} | 2 +- ...ory.java => InMemoryTicketRepository.java} | 2 +- ...ionsImpl.java => StdOutNotifications.java} | 2 +- ...ryServiceImpl.java => ConsoleService.java} | 4 +- .../hexagonal/LotteryTestingModule.java | 20 +- .../hexagonal/banking/WireTransfersTest.java | 2 +- .../database/LotteryTicketRepositoryTest.java | 6 +- .../hexagonal/domain/LotteryTest.java | 5 - 13 files changed, 158 insertions(+), 210 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/administration/{LotteryAdministrationImpl.java => ConsoleAdministration.java} (93%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/banking/{WireTransfersImpl.java => InMemoryBank.java} (97%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/database/{LotteryTicketInMemoryRepository.java => InMemoryTicketRepository.java} (96%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/{LotteryNotificationsImpl.java => StdOutNotifications.java} (97%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/service/{LotteryServiceImpl.java => ConsoleService.java} (94%) diff --git a/hexagonal/etc/hexagonal.png b/hexagonal/etc/hexagonal.png index 8c03d375f998e2cd843e7a1dbc3726dc3720c8a0..1ad077fd4fd0e9874c2f85f3f488ead2e3fe7ffc 100644 GIT binary patch literal 148602 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�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 A!2kdN literal 165940 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

    A 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 nubE?DSO9EB!|HX0hPM9 - - + - + - - + - - + - + - - + - - + - + - - + - - + - + - - + - - + + + + + + + + - + - - + - - + - - + - - - - - - - - - + @@ -146,174 +145,130 @@ - + - - - - - - - - - - - - - - - - + - - + - - - - + + + + - + - + - - + + - + + + + + + + + + - - - + + + - - - - - - - - - + - - - - - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + - - - - + + + + + + + + - + - - - - - - - - + + + + - + - - - - + + + + - + - - - - + + + + + + + + - + - - - - - - - - - - - - - - - - + + + + - + accounts = new HashMap<>(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java similarity index 96% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java index fe17d8cdf620..0d510b411731 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java @@ -34,7 +34,7 @@ * Mock database for lottery tickets. * */ -public class LotteryTicketInMemoryRepository implements LotteryTicketRepository { +public class InMemoryTicketRepository implements LotteryTicketRepository { private static Map tickets = new HashMap<>(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java similarity index 97% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java index c59a47970cf1..f6bd3b546259 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java @@ -24,7 +24,7 @@ import com.iluwatar.hexagonal.domain.PlayerDetails; -public class LotteryNotificationsImpl implements LotteryNotifications { +public class StdOutNotifications implements LotteryNotifications { @Override public void notifyTicketSubmitted(PlayerDetails details) { diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java similarity index 94% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java index c912bb0b4d1f..6804f21c5f5a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java @@ -36,7 +36,7 @@ * Implementation for lottery service * */ -public class LotteryServiceImpl implements LotteryService { +public class ConsoleService implements LotteryService { private final LotterySystem lotterySystem; @@ -44,7 +44,7 @@ public class LotteryServiceImpl implements LotteryService { * Constructor */ @Inject - public LotteryServiceImpl(LotterySystem lotterySystem) { + public ConsoleService(LotterySystem lotterySystem) { this.lotterySystem = lotterySystem; } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java index 5fbeb8240471..c401467b54c5 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -23,18 +23,18 @@ package com.iluwatar.hexagonal; import com.google.inject.AbstractModule; +import com.iluwatar.hexagonal.administration.ConsoleAdministration; import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.domain.LotterySystem; import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.notifications.StdOutNotifications; +import com.iluwatar.hexagonal.service.ConsoleService; import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; /** * Guice module for testing dependencies @@ -42,11 +42,11 @@ public class LotteryTestingModule extends AbstractModule { @Override protected void configure() { - bind(LotteryTicketRepository.class).to(LotteryTicketInMemoryRepository.class); + bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); bind(LotterySystem.class).to(LotterySystemImpl.class); - bind(LotteryNotifications.class).to(LotteryNotificationsImpl.class); - bind(WireTransfers.class).to(WireTransfersImpl.class); - bind(LotteryAdministration.class).to(LotteryAdministrationImpl.class); - bind(LotteryService.class).to(LotteryServiceImpl.class); + bind(LotteryNotifications.class).to(StdOutNotifications.class); + bind(WireTransfers.class).to(InMemoryBank.class); + bind(LotteryAdministration.class).to(ConsoleAdministration.class); + bind(LotteryService.class).to(ConsoleService.class); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java index 0274feca39b3..25fbf460c3a4 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java @@ -34,7 +34,7 @@ */ public class WireTransfersTest { - private final WireTransfers bank = new WireTransfersImpl(); + private final WireTransfers bank = new InMemoryBank(); @Test public void testInit() { diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java index b20e928c865b..31cb8f5f07a2 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -30,8 +30,6 @@ import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.test.LotteryTestUtils; @@ -43,7 +41,7 @@ */ public class LotteryTicketRepositoryTest { - private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + private final LotteryTicketRepository repository = new InMemoryTicketRepository(); @Before public void clear() { @@ -52,7 +50,7 @@ public void clear() { @Test public void testCrudOperations() { - LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + LotteryTicketRepository repository = new InMemoryTicketRepository(); assertEquals(repository.findAll().size(), 0); LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); Optional id = repository.save(ticket); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 331cc0d66697..732f983052da 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -33,16 +33,11 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; -import com.iluwatar.hexagonal.LotteryModule; import com.iluwatar.hexagonal.LotteryTestingModule; -import com.iluwatar.hexagonal.domain.*; import org.junit.Before; import org.junit.Test; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; import com.iluwatar.hexagonal.test.LotteryTestUtils; From adc6019c7e19a958fb775a073c61214f7ca01351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 10 Sep 2016 07:14:24 +0300 Subject: [PATCH 167/207] Hexagonal pattern: Remove interfaces with only one implementation --- hexagonal/etc/hexagonal.ucls | 6 +- .../com/iluwatar/hexagonal/LotteryModule.java | 8 -- .../administration/ConsoleAdministration.java | 61 ---------- .../administration/LotteryAdministration.java | 26 +++- .../hexagonal/domain/LotterySystem.java | 80 +++++++++++-- .../hexagonal/domain/LotterySystemImpl.java | 112 ------------------ .../hexagonal/service/ConsoleService.java | 60 ---------- .../hexagonal/service/LotteryService.java | 28 +++-- .../hexagonal/LotteryTestingModule.java | 8 -- 9 files changed, 115 insertions(+), 274 deletions(-) delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls index 8f4481cbd84c..f698c0dd2ea1 100644 --- a/hexagonal/etc/hexagonal.ucls +++ b/hexagonal/etc/hexagonal.ucls @@ -22,7 +22,7 @@ - @@ -62,7 +62,7 @@ - @@ -122,7 +122,7 @@ - diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java index 978d7ce9f840..992e663575d8 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java @@ -23,17 +23,12 @@ package com.iluwatar.hexagonal; import com.google.inject.AbstractModule; -import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.ConsoleAdministration; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.ConsoleService; import com.iluwatar.hexagonal.service.LotteryService; /** @@ -43,10 +38,7 @@ public class LotteryModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); - bind(LotterySystem.class).to(LotterySystemImpl.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); bind(WireTransfers.class).to(InMemoryBank.class); - bind(LotteryAdministration.class).to(ConsoleAdministration.class); - bind(LotteryService.class).to(ConsoleService.class); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java deleted file mode 100644 index 1526fc8d6ff5..000000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 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.hexagonal.administration; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketId; - -import java.util.Map; - -/** - * - * Lottery administration implementation - * - */ -public class ConsoleAdministration implements LotteryAdministration { - - private final LotterySystem lotterySystem; - - @Inject - public ConsoleAdministration(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; - } - - @Override - public Map getAllSubmittedTickets() { - return lotterySystem.getAllSubmittedTickets(); - } - - @Override - public LotteryNumbers performLottery() { - return lotterySystem.performLottery(); - } - - @Override - public void resetLottery() { - lotterySystem.resetLottery(); - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index c6c034ac9235..a59461a48d57 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -22,7 +22,9 @@ */ package com.iluwatar.hexagonal.administration; +import com.google.inject.Inject; import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotterySystem; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; @@ -30,24 +32,36 @@ /** * - * Administrator interface for lottery service. + * Lottery administration implementation * */ -public interface LotteryAdministration { +public class LotteryAdministration { + + private final LotterySystem lotterySystem; + + @Inject + public LotteryAdministration(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; + } /** * Get all the lottery tickets submitted for lottery */ - Map getAllSubmittedTickets(); + public Map getAllSubmittedTickets() { + return lotterySystem.getAllSubmittedTickets(); + } /** * Draw lottery numbers */ - LotteryNumbers performLottery(); + public LotteryNumbers performLottery() { + return lotterySystem.performLottery(); + } /** * Begin new lottery round */ - void resetLottery(); - + public void resetLottery() { + lotterySystem.resetLottery(); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java index 2ee114556f00..ed58c82db3f9 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java @@ -22,37 +22,101 @@ */ package com.iluwatar.hexagonal.domain; +import com.google.inject.Inject; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; + import java.util.Map; import java.util.Optional; /** - * Lottery system interface + * Lottery system */ -public interface LotterySystem { +public class LotterySystem { + + private final LotteryTicketRepository repository; + private final LotteryNotifications notifications; + private final WireTransfers wireTransfers; + + /** + * Constructor + */ + @Inject + public LotterySystem(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { + this.repository = repository; + this.notifications = notifications; + this.wireTransfers = wireTransfers; + } /** * Get all the lottery tickets submitted for lottery */ - Map getAllSubmittedTickets(); + public Map getAllSubmittedTickets() { + return repository.findAll(); + } /** * Draw lottery numbers */ - LotteryNumbers performLottery(); + public LotteryNumbers performLottery() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id : tickets.keySet()) { + LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, + LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); + if (transferred) { + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } else { + notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; + } /** * Begin new lottery round */ - void resetLottery(); + public void resetLottery() { + repository.deleteAll(); + } /** * Submit lottery ticket to participate in the lottery */ - Optional submitTicket(LotteryTicket ticket); + public Optional submitTicket(LotteryTicket ticket) { + boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, + ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; + } /** * Check if lottery ticket has won */ - LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); - + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); + } + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java deleted file mode 100644 index e37185143420..000000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 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.hexagonal.domain; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; - -import java.util.Map; -import java.util.Optional; - -/** - * Lottery system implementation - */ -public class LotterySystemImpl implements LotterySystem { - - private final LotteryTicketRepository repository; - private final LotteryNotifications notifications; - private final WireTransfers wireTransfers; - - /** - * Constructor - */ - @Inject - public LotterySystemImpl(LotteryTicketRepository repository, LotteryNotifications notifications, - WireTransfers wireTransfers) { - this.repository = repository; - this.notifications = notifications; - this.wireTransfers = wireTransfers; - } - - @Override - public Map getAllSubmittedTickets() { - return repository.findAll(); - } - - @Override - public LotteryNumbers performLottery() { - LotteryNumbers numbers = LotteryNumbers.createRandom(); - Map tickets = getAllSubmittedTickets(); - for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); - if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, - LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); - if (transferred) { - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } else { - notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } - } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); - } - } - return numbers; - } - - @Override - public void resetLottery() { - repository.deleteAll(); - } - - @Override - public Optional submitTicket(LotteryTicket ticket) { - boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, - ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); - if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); - return Optional.empty(); - } - Optional optional = repository.save(ticket); - if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); - } - return optional; - } - - @Override - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - Optional optional = repository.findById(id); - if (optional.isPresent()) { - if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); - } - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); - } - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java deleted file mode 100644 index 6804f21c5f5a..000000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 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.hexagonal.service; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; - -import java.util.Optional; - -/** - * - * Implementation for lottery service - * - */ -public class ConsoleService implements LotteryService { - - private final LotterySystem lotterySystem; - - /** - * Constructor - */ - @Inject - public ConsoleService(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; - } - - @Override - public Optional submitTicket(LotteryTicket ticket) { - return lotterySystem.submitTicket(ticket); - } - - @Override - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return lotterySystem.checkTicketForPrize(id, winningNumbers); - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index ef2202968056..80d306b043ab 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -22,27 +22,39 @@ */ package com.iluwatar.hexagonal.service; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.google.inject.Inject; +import com.iluwatar.hexagonal.domain.*; import java.util.Optional; /** * - * Interface for submitting and checking lottery tickets. + * Implementation for lottery service * */ -public interface LotteryService { +public class LotteryService { + + private final LotterySystem lotterySystem; + + /** + * Constructor + */ + @Inject + public LotteryService(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; + } /** * Submit lottery ticket to participate in the lottery */ - Optional submitTicket(LotteryTicket ticket); + public Optional submitTicket(LotteryTicket ticket) { + return lotterySystem.submitTicket(ticket); + } /** * Check if lottery ticket has won */ - LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + return lotterySystem.checkTicketForPrize(id, winningNumbers); + } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java index c401467b54c5..252e3e66d1ab 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -23,17 +23,12 @@ package com.iluwatar.hexagonal; import com.google.inject.AbstractModule; -import com.iluwatar.hexagonal.administration.ConsoleAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministration; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.ConsoleService; import com.iluwatar.hexagonal.service.LotteryService; /** @@ -43,10 +38,7 @@ public class LotteryTestingModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); - bind(LotterySystem.class).to(LotterySystemImpl.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); bind(WireTransfers.class).to(InMemoryBank.class); - bind(LotteryAdministration.class).to(ConsoleAdministration.class); - bind(LotteryService.class).to(ConsoleService.class); } } From 121ed3cca896d5870225565e50142c23f2cd4f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 10 Sep 2016 07:56:37 +0300 Subject: [PATCH 168/207] Hexagonal pattern: Move lottery administration and service to the core. Introduce console interfaces for players and administartors. --- hexagonal/etc/hexagonal.ucls | 8 +-- .../main/java/com/iluwatar/hexagonal/App.java | 4 +- .../com/iluwatar/hexagonal/LotteryModule.java | 1 - .../administration/ConsoleAdministration.java | 7 ++ .../administration/LotteryAdministration.java | 67 ------------------- ...System.java => LotteryAdministration.java} | 51 +++----------- .../{service => domain}/LotteryService.java | 33 +++++++-- .../domain/LotteryTicketChecker.java | 33 +++++++++ .../hexagonal/service/ConsoleLottery.java | 7 ++ .../hexagonal/LotteryTestingModule.java | 1 - .../hexagonal/domain/LotteryTest.java | 28 ++++---- 11 files changed, 103 insertions(+), 137 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java rename hexagonal/src/main/java/com/iluwatar/hexagonal/domain/{LotterySystem.java => LotteryAdministration.java} (64%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{service => domain}/LotteryService.java (55%) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls index f698c0dd2ea1..0318576c2c27 100644 --- a/hexagonal/etc/hexagonal.ucls +++ b/hexagonal/etc/hexagonal.ucls @@ -62,7 +62,7 @@ - @@ -102,7 +102,7 @@ - @@ -122,7 +122,7 @@ - @@ -142,7 +142,7 @@ - diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index a1bb26454465..83ae150327ef 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -28,13 +28,13 @@ import com.google.inject.Guice; import com.google.inject.Injector; -import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.domain.LotteryAdministration; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.PlayerDetails; -import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.domain.LotteryService; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java index 992e663575d8..dbaf494c2473 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java @@ -29,7 +29,6 @@ import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.LotteryService; /** * Guice module for binding production dependencies diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java new file mode 100644 index 000000000000..5238edd244da --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -0,0 +1,7 @@ +package com.iluwatar.hexagonal.administration; + +/** + * Console interface for lottery administration + */ +public class ConsoleAdministration { +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java deleted file mode 100644 index a59461a48d57..000000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 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.hexagonal.administration; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketId; - -import java.util.Map; - -/** - * - * Lottery administration implementation - * - */ -public class LotteryAdministration { - - private final LotterySystem lotterySystem; - - @Inject - public LotteryAdministration(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; - } - - /** - * Get all the lottery tickets submitted for lottery - */ - public Map getAllSubmittedTickets() { - return lotterySystem.getAllSubmittedTickets(); - } - - /** - * Draw lottery numbers - */ - public LotteryNumbers performLottery() { - return lotterySystem.performLottery(); - } - - /** - * Begin new lottery round - */ - public void resetLottery() { - lotterySystem.resetLottery(); - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java similarity index 64% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index ed58c82db3f9..7117397d32f6 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -28,26 +28,26 @@ import com.iluwatar.hexagonal.notifications.LotteryNotifications; import java.util.Map; -import java.util.Optional; /** - * Lottery system + * + * Lottery administration implementation + * */ -public class LotterySystem { +public class LotteryAdministration { private final LotteryTicketRepository repository; private final LotteryNotifications notifications; private final WireTransfers wireTransfers; + private final LotteryTicketChecker checker; - /** - * Constructor - */ @Inject - public LotterySystem(LotteryTicketRepository repository, LotteryNotifications notifications, - WireTransfers wireTransfers) { + public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { this.repository = repository; this.notifications = notifications; this.wireTransfers = wireTransfers; + this.checker = new LotteryTicketChecker(this.repository); } /** @@ -64,7 +64,7 @@ public LotteryNumbers performLottery() { LotteryNumbers numbers = LotteryNumbers.createRandom(); Map tickets = getAllSubmittedTickets(); for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); + LotteryTicketCheckResult result = checker.checkTicketForPrize(id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); @@ -86,37 +86,4 @@ public LotteryNumbers performLottery() { public void resetLottery() { repository.deleteAll(); } - - /** - * Submit lottery ticket to participate in the lottery - */ - public Optional submitTicket(LotteryTicket ticket) { - boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, - ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); - if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); - return Optional.empty(); - } - Optional optional = repository.save(ticket); - if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); - } - return optional; - } - - /** - * Check if lottery ticket has won - */ - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - Optional optional = repository.findById(id); - if (optional.isPresent()) { - if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); - } - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); - } - } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java similarity index 55% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 80d306b043ab..76cd47d1dcb4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -20,10 +20,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.service; +package com.iluwatar.hexagonal.domain; import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.*; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; import java.util.Optional; @@ -34,27 +36,44 @@ */ public class LotteryService { - private final LotterySystem lotterySystem; + private final LotteryTicketRepository repository; + private final LotteryNotifications notifications; + private final WireTransfers wireTransfers; + private final LotteryTicketChecker checker; /** * Constructor */ @Inject - public LotteryService(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; + public LotteryService(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { + this.repository = repository; + this.notifications = notifications; + this.wireTransfers = wireTransfers; + this.checker = new LotteryTicketChecker(this.repository); } /** * Submit lottery ticket to participate in the lottery */ public Optional submitTicket(LotteryTicket ticket) { - return lotterySystem.submitTicket(ticket); + boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, + ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; } /** * Check if lottery ticket has won */ public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return lotterySystem.checkTicketForPrize(id, winningNumbers); + return checker.checkTicketForPrize(id, winningNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java new file mode 100644 index 000000000000..10042528bd2f --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java @@ -0,0 +1,33 @@ +package com.iluwatar.hexagonal.domain; + +import com.iluwatar.hexagonal.database.LotteryTicketRepository; + +import java.util.Optional; + +/** + * Lottery ticket checker + */ +public class LotteryTicketChecker { + + private final LotteryTicketRepository repository; + + public LotteryTicketChecker(LotteryTicketRepository repository) { + this.repository = repository; + } + + /** + * Check if lottery ticket has won + */ + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java new file mode 100644 index 000000000000..e48410982f44 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -0,0 +1,7 @@ +package com.iluwatar.hexagonal.service; + +/** + * Console interface for lottery players + */ +public class ConsoleLottery { +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java index 252e3e66d1ab..22bfa8f01d73 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -29,7 +29,6 @@ import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.LotteryService; /** * Guice module for testing dependencies diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 732f983052da..ed6f8d180672 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -50,7 +50,9 @@ public class LotteryTest { private Injector injector; @Inject - private LotterySystem lotterySystem; + private LotteryAdministration administration; + @Inject + private LotteryService service; @Inject private WireTransfers wireTransfers; @@ -68,34 +70,34 @@ public void setup() { @Test public void testLottery() { // admin resets the lottery - lotterySystem.resetLottery(); - assertEquals(lotterySystem.getAllSubmittedTickets().size(), 0); + administration.resetLottery(); + assertEquals(administration.getAllSubmittedTickets().size(), 0); // players submit the lottery tickets - Optional ticket1 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); assertTrue(ticket1.isPresent()); - Optional ticket2 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); assertTrue(ticket2.isPresent()); - Optional ticket3 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); assertTrue(ticket3.isPresent()); - assertEquals(lotterySystem.getAllSubmittedTickets().size(), 3); + assertEquals(administration.getAllSubmittedTickets().size(), 3); // perform lottery - LotteryNumbers winningNumbers = lotterySystem.performLottery(); + LotteryNumbers winningNumbers = administration.performLottery(); // cheat a bit for testing sake, use winning numbers to submit another ticket - Optional ticket4 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", "123-12312", "+12421255", winningNumbers.getNumbers())); assertTrue(ticket4.isPresent()); - assertEquals(lotterySystem.getAllSubmittedTickets().size(), 4); + assertEquals(administration.getAllSubmittedTickets().size(), 4); // check winners - Map tickets = lotterySystem.getAllSubmittedTickets(); + Map tickets = administration.getAllSubmittedTickets(); for (LotteryTicketId id: tickets.keySet()) { - LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(id, winningNumbers); + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { assertTrue(checkResult.getPrizeAmount() > 0); @@ -105,7 +107,7 @@ public void testLottery() { } // check another ticket that has not been submitted - LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); assertEquals(checkResult.getPrizeAmount(), 0); } From e17d72bca81523ae4521cc4b198e72980aad8b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 10 Sep 2016 14:28:05 +0300 Subject: [PATCH 169/207] Hexagonal pattern: Added console interfaces for players and administration. --- .../main/java/com/iluwatar/hexagonal/App.java | 81 +---------- .../administration/ConsoleAdministration.java | 77 +++++++++++ .../domain/LotteryAdministration.java | 3 + .../hexagonal/domain/LotteryNumbers.java | 7 +- .../hexagonal/domain/LotteryTicket.java | 5 + .../domain/LotteryTicketChecker.java | 22 +++ .../hexagonal/domain/LotteryTicketId.java | 9 ++ .../hexagonal/domain/PlayerDetails.java | 7 + .../hexagonal/{ => module}/LotteryModule.java | 2 +- .../module}/LotteryTestingModule.java | 2 +- .../hexagonal/sampledata/SampleData.java | 107 ++++++++++++++ .../hexagonal/service/ConsoleLottery.java | 130 ++++++++++++++++++ .../hexagonal/domain/LotteryTest.java | 2 +- 13 files changed, 374 insertions(+), 80 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{ => module}/LotteryModule.java (97%) rename hexagonal/src/{test/java/com/iluwatar/hexagonal => main/java/com/iluwatar/hexagonal/module}/LotteryTestingModule.java (97%) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 83ae150327ef..a7d31446bb48 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -22,19 +22,12 @@ */ package com.iluwatar.hexagonal; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - import com.google.inject.Guice; import com.google.inject.Injector; import com.iluwatar.hexagonal.domain.LotteryAdministration; -import com.iluwatar.hexagonal.banking.InMemoryBank; -import com.iluwatar.hexagonal.domain.LotteryConstants; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.module.LotteryTestingModule; +import com.iluwatar.hexagonal.sampledata.SampleData; /** * @@ -68,64 +61,13 @@ * */ public class App { - - private static final List PLAYERS; - - static { - PLAYERS = new ArrayList<>(); - PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); - PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); - PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); - PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); - PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); - PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); - PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); - PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); - PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); - PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); - PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); - PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); - PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); - PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); - PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); - PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); - PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); - PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); - PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); - PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); - PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); - PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); - PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); - PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); - PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); - PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); - PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); - PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); - PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); - PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); - PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); - PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); - PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); - PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); - PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); - PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); - PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); - InMemoryBank wireTransfers = new InMemoryBank(); - Random random = new Random(); - for (int i = 0; i < PLAYERS.size(); i++) { - wireTransfers.setFunds(PLAYERS.get(i).getBankAccount(), - random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); - } - } - + /** * Program entry point */ public static void main(String[] args) { - Injector injector = Guice.createInjector(new LotteryModule()); + Injector injector = Guice.createInjector(new LotteryTestingModule()); // start new lottery round LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); @@ -133,22 +75,9 @@ public static void main(String[] args) { // submit some lottery tickets LotteryService service = injector.getInstance(LotteryService.class); - submitTickets(service, 20); + SampleData.submitTickets(service, 20); // perform lottery administartion.performLottery(); } - - private static void submitTickets(LotteryService lotteryService, int numTickets) { - for (int i = 0; i < numTickets; i++) { - LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); - lotteryService.submitTicket(ticket); - } - } - - private static PlayerDetails getRandomPlayerDetails() { - Random random = new Random(); - int idx = random.nextInt(PLAYERS.size()); - return PLAYERS.get(idx); - } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index 5238edd244da..4301c07e3fa4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -1,7 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.administration; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.iluwatar.hexagonal.domain.LotteryAdministration; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.sampledata.SampleData; + +import java.util.Scanner; + /** * Console interface for lottery administration */ public class ConsoleAdministration { + + /** + * Program entry point + */ + public static void main(String[] args) { + Injector injector = Guice.createInjector(new LotteryModule()); + LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); + LotteryService service = injector.getInstance(LotteryService.class); + SampleData.submitTickets(service, 20); + Scanner scanner = new Scanner(System.in); + boolean exit = false; + while (!exit) { + printMainMenu(); + String cmd = readString(scanner); + if (cmd.equals("1")) { + administartion.getAllSubmittedTickets().forEach((k,v)->System.out.println("Key: " + k + " Value: " + v)); + } else if (cmd.equals("2")) { + LotteryNumbers numbers = administartion.performLottery(); + System.out.println("The winning numbers: " + numbers); + System.out.println("Time to reset the database for next round, eh?"); + } else if (cmd.equals("3")) { + administartion.resetLottery(); + System.out.println("The lottery ticket database was cleared."); + } else if (cmd.equals("4")) { + exit = true; + } else { + System.out.println("Unknown command: " + cmd); + } + } + } + + private static void printMainMenu() { + System.out.println(""); + System.out.println("### Lottery Administration Console ###"); + System.out.println("(1) Show all submitted tickets"); + System.out.println("(2) Perform lottery draw"); + System.out.println("(3) Reset lottery ticket database"); + System.out.println("(4) Exit"); + } + + private static String readString(Scanner scanner) { + System.out.print("> "); + String cmd = scanner.next(); + return cmd; + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 7117397d32f6..f7339086342d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -41,6 +41,9 @@ public class LotteryAdministration { private final WireTransfers wireTransfers; private final LotteryTicketChecker checker; + /** + * Constructor + */ @Inject public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications, WireTransfers wireTransfers) { diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java index 69d6546578a9..9ce8d61bdacc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -92,7 +92,12 @@ private void generateRandomNumbers() { } } } - + + @Override + public String toString() { + return "LotteryNumbers{" + "numbers=" + numbers + '}'; + } + /** * * Helper class for generating random numbers. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java index e5828cfbf7c4..08064f46cbbf 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -61,6 +61,11 @@ public LotteryNumbers getNumbers() { return lotteryNumbers; } + @Override + public String toString() { + return playerDetails.toString() + " " + lotteryNumbers.toString(); + } + @Override public int hashCode() { final int prime = 31; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java index 10042528bd2f..ce193386b9ad 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; import com.iluwatar.hexagonal.database.LotteryTicketRepository; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java index 710091222b2d..e2aa51792085 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -34,8 +34,17 @@ public class LotteryTicketId { public LotteryTicketId() { id = UUID.randomUUID(); } + + public LotteryTicketId(String str) { + id = UUID.fromString(str); + } public UUID getId() { return id; } + + @Override + public String toString() { + return id.toString(); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java index 2fcdb6eb342b..1061ad5535ce 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -70,6 +70,13 @@ public String getPhoneNumber() { return phoneNumber; } + @Override + public String toString() { + return "PlayerDetails{" + "emailAddress='" + emailAddress + '\'' + + ", bankAccountNumber='" + bankAccountNumber + '\'' + + ", phoneNumber='" + phoneNumber + '\'' + '}'; + } + @Override public int hashCode() { final int prime = 31; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java similarity index 97% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index dbaf494c2473..c62dbfa54cd8 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal; +package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; import com.iluwatar.hexagonal.banking.InMemoryBank; diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java similarity index 97% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java index 22bfa8f01d73..c934ed43cdcc 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal; +package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; import com.iluwatar.hexagonal.banking.InMemoryBank; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java new file mode 100644 index 000000000000..1af18118dc27 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java @@ -0,0 +1,107 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.sampledata; + +import com.iluwatar.hexagonal.banking.InMemoryBank; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Utilities for creating sample lottery tickets + */ +public class SampleData { + + private static final List PLAYERS; + + static { + PLAYERS = new ArrayList<>(); + PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + InMemoryBank wireTransfers = new InMemoryBank(); + Random random = new Random(); + for (int i = 0; i < PLAYERS.size(); i++) { + wireTransfers.setFunds(PLAYERS.get(i).getBankAccount(), + random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); + } + } + + /** + * Inserts lottery tickets into the database based on the sample data + */ + public static void submitTickets(LotteryService lotteryService, int numTickets) { + for (int i = 0; i < numTickets; i++) { + LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); + lotteryService.submitTicket(ticket); + } + } + + private static PlayerDetails getRandomPlayerDetails() { + Random random = new Random(); + int idx = random.nextInt(PLAYERS.size()); + return PLAYERS.get(idx); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index e48410982f44..aade24a91553 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -1,7 +1,137 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.service; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.sampledata.SampleData; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Scanner; +import java.util.Set; + /** * Console interface for lottery players */ public class ConsoleLottery { + + + /** + * Program entry point + */ + public static void main(String[] args) { + Injector injector = Guice.createInjector(new LotteryModule()); + LotteryService service = injector.getInstance(LotteryService.class); + WireTransfers bank = injector.getInstance(WireTransfers.class); + SampleData.submitTickets(service, 20); + Scanner scanner = new Scanner(System.in); + boolean exit = false; + while (!exit) { + printMainMenu(); + String cmd = readString(scanner); + if (cmd.equals("1")) { + System.out.println("What is the account number?"); + String account = readString(scanner); + System.out.println(String.format("The account %s has %d credits.", account, bank.getFunds(account))); + } else if (cmd.equals("2")) { + System.out.println("What is the account number?"); + String account = readString(scanner); + System.out.println("How many credits do you want to deposit?"); + String amount = readString(scanner); + bank.setFunds(account, Integer.parseInt(amount)); + System.out.println(String.format("The account %s now has %d credits.", account, bank.getFunds(account))); + } else if (cmd.equals("3")) { + System.out.println("What is your email address?"); + String email = readString(scanner); + System.out.println("What is your bank account number?"); + String account = readString(scanner); + System.out.println("What is your phone number?"); + String phone = readString(scanner); + PlayerDetails details = PlayerDetails.create(email, account, phone); + System.out.println("Give 4 comma separated lottery numbers?"); + String numbers = readString(scanner); + String[] parts = numbers.split(","); + Set chosen = new HashSet<>(); + for (int i = 0; i < 4; i++) { + chosen.add(Integer.parseInt(parts[i])); + } + LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); + LotteryTicket lotteryTicket = LotteryTicket.create(details, lotteryNumbers); + Optional id = service.submitTicket(lotteryTicket); + if (id.isPresent()) { + System.out.println("Submitted lottery ticket with id: " + id.get()); + } else { + System.out.println("Failed submitting lottery ticket - please try again."); + } + } else if (cmd.equals("4")) { + System.out.println("What is the ID of the lottery ticket?"); + String id = readString(scanner); + System.out.println("Give the 4 comma separated winning numbers?"); + String numbers = readString(scanner); + String[] parts = numbers.split(","); + Set winningNumbers = new HashSet<>(); + for (int i = 0; i < 4; i++) { + winningNumbers.add(Integer.parseInt(parts[i])); + } + LotteryTicketCheckResult result = service.checkTicketForPrize( + new LotteryTicketId(id), LotteryNumbers.create(winningNumbers)); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + System.out.println("Congratulations! The lottery ticket has won!"); + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + System.out.println("Unfortunately the lottery ticket did not win."); + } else { + System.out.println("Such lottery ticket has not been submitted."); + } + } else if (cmd.equals("5")) { + exit = true; + } else { + System.out.println("Unknown command"); + } + } + } + + private static void printMainMenu() { + System.out.println(""); + System.out.println("### Lottery Service Console ###"); + System.out.println("(1) Query lottery account funds"); + System.out.println("(2) Add funds to lottery account"); + System.out.println("(3) Submit ticket"); + System.out.println("(4) Check ticket"); + System.out.println("(5) Exit"); + } + + private static String readString(Scanner scanner) { + System.out.print("> "); + String cmd = scanner.next(); + return cmd; + } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index ed6f8d180672..943440f079ae 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -33,7 +33,7 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; -import com.iluwatar.hexagonal.LotteryTestingModule; +import com.iluwatar.hexagonal.module.LotteryTestingModule; import org.junit.Before; import org.junit.Test; From 4410419914ee89c7eea2382ae49b056451b420cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 10:34:12 +0300 Subject: [PATCH 170/207] Hexagonal pattern: Simplified lottery ticket ids --- .../hexagonal/domain/LotteryTicketId.java | 38 ++++++++++++---- .../hexagonal/service/ConsoleLottery.java | 4 +- .../hexagonal/domain/LotteryTicketIdTest.java | 45 +++++++++++++++++++ 3 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java index e2aa51792085..2f8a59bbaa46 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -22,29 +22,49 @@ */ package com.iluwatar.hexagonal.domain; -import java.util.UUID; - /** * Lottery ticked id */ public class LotteryTicketId { - - private final UUID id; + + private static volatile int numAllocated; + private final int id; public LotteryTicketId() { - id = UUID.randomUUID(); + this.id = numAllocated + 1; + numAllocated++; } - public LotteryTicketId(String str) { - id = UUID.fromString(str); + public LotteryTicketId(int id) { + this.id = id; } - public UUID getId() { + public int getId() { return id; } @Override public String toString() { - return id.toString(); + return String.format("%d", id); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + LotteryTicketId that = (LotteryTicketId) o; + + return id == that.id; + + } + + @Override + public int hashCode() { + return id; } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index aade24a91553..5463eca7c447 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -32,7 +32,6 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.module.LotteryModule; -import com.iluwatar.hexagonal.sampledata.SampleData; import java.util.HashSet; import java.util.Optional; @@ -52,7 +51,6 @@ public static void main(String[] args) { Injector injector = Guice.createInjector(new LotteryModule()); LotteryService service = injector.getInstance(LotteryService.class); WireTransfers bank = injector.getInstance(WireTransfers.class); - SampleData.submitTickets(service, 20); Scanner scanner = new Scanner(System.in); boolean exit = false; while (!exit) { @@ -103,7 +101,7 @@ public static void main(String[] args) { winningNumbers.add(Integer.parseInt(parts[i])); } LotteryTicketCheckResult result = service.checkTicketForPrize( - new LotteryTicketId(id), LotteryNumbers.create(winningNumbers)); + new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { System.out.println("Congratulations! The lottery ticket has won!"); } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java new file mode 100644 index 000000000000..c5e4c1541fbf --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for lottery ticket id + */ +public class LotteryTicketIdTest { + + @Test + public void testEquals() { + LotteryTicketId ticketId1 = new LotteryTicketId(); + LotteryTicketId ticketId2 = new LotteryTicketId(); + LotteryTicketId ticketId3 = new LotteryTicketId(); + assertFalse(ticketId1.equals(ticketId2)); + assertFalse(ticketId2.equals(ticketId3)); + LotteryTicketId ticketId4 = new LotteryTicketId(ticketId1.getId()); + assertTrue(ticketId1.equals(ticketId4)); + } +} From a85463470ea3a679ac69e6e1c43c2cb7ae512139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 13:53:00 +0300 Subject: [PATCH 171/207] Hexagonal pattern: Add mongo driver dependency --- hexagonal/pom.xml | 4 ++++ pom.xml | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index a73fea48d748..ce1a7049a8f3 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -43,5 +43,9 @@ com.google.inject guice + + org.mongodb + mongo-java-driver + diff --git a/pom.xml b/pom.xml index 023579e9722b..b1ae7d811785 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ 2.22 1.4.1 4.0 + 3.3.0 abstract-factory @@ -242,6 +243,11 @@ guice ${guice.version} + + org.mongodb + mongo-java-driver + ${mongo-java-driver.version} + From 626c56730c7dac32eb33bd414ad4a9436736a097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 20:02:34 +0300 Subject: [PATCH 172/207] Hexagonal pattern: Added Mongo based ticket repository and set production configuration to use that --- .../administration/ConsoleAdministration.java | 2 +- .../database/MongoTicketRepository.java | 194 ++++++++++++++++++ .../hexagonal/domain/LotteryNumbers.java | 18 ++ .../hexagonal/domain/LotteryTicket.java | 24 ++- .../hexagonal/module/LotteryModule.java | 4 +- .../hexagonal/sampledata/SampleData.java | 4 +- .../hexagonal/service/ConsoleLottery.java | 2 +- ...java => InMemoryTicketRepositoryTest.java} | 2 +- .../database/MongoTicketRepositoryTest.java | 94 +++++++++ .../hexagonal/domain/LotteryTicketTest.java | 6 +- .../hexagonal/test/LotteryTestUtils.java | 3 +- 11 files changed, 339 insertions(+), 14 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/database/{LotteryTicketRepositoryTest.java => InMemoryTicketRepositoryTest.java} (98%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index 4301c07e3fa4..6a846280c4f0 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -54,7 +54,7 @@ public static void main(String[] args) { administartion.getAllSubmittedTickets().forEach((k,v)->System.out.println("Key: " + k + " Value: " + v)); } else if (cmd.equals("2")) { LotteryNumbers numbers = administartion.performLottery(); - System.out.println("The winning numbers: " + numbers); + System.out.println("The winning numbers: " + numbers.getNumbersAsString()); System.out.println("Time to reset the database for next round, eh?"); } else if (cmd.equals("3")) { administartion.resetLottery(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java new file mode 100644 index 000000000000..ff0439af886c --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -0,0 +1,194 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.database; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.bson.Document; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; + +/** + * Mongo lottery ticket database + */ +public class MongoTicketRepository implements LotteryTicketRepository { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 27017; + private static final String DEFAULT_DB = "lotteryDB"; + private static final String DEFAULT_TICKETS_COLLECTION = "lotteryTickets"; + private static final String DEFAULT_COUNTERS_COLLECTION = "counters"; + + private MongoClient mongoClient; + private MongoDatabase database; + private MongoCollection ticketsCollection; + private MongoCollection countersCollection; + + /** + * Constructor + */ + public MongoTicketRepository() { + connect(); + } + + /** + * Constructor accepting parameters + */ + public MongoTicketRepository(String host, int port, String dbName, String ticketsCollectionName, + String countersCollectionName) { + connect(host, port, dbName, ticketsCollectionName, countersCollectionName); + } + + /** + * Connect to database with default parameters + */ + public void connect() { + connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); + } + + /** + * Connect to database with given parameters + */ + public void connect(String host, int port, String dbName, String ticketsCollectionName, + String countersCollectionName) { + if (mongoClient != null) { + mongoClient.close(); + } + mongoClient = new MongoClient(host , port); + database = mongoClient.getDatabase(dbName); + ticketsCollection = database.getCollection(ticketsCollectionName); + countersCollection = database.getCollection(countersCollectionName); + if (countersCollection.count() <= 0) { + initCounters(); + } + } + + private void initCounters() { + Document doc = new Document("_id", "ticketId").append("seq", 1); + countersCollection.insertOne(doc); + } + + /** + * @return next ticket id + */ + public int getNextId() { + Document find = new Document("_id", "ticketId"); + Document increase = new Document("seq", 1); + Document update = new Document("$inc", increase); + Document result = countersCollection.findOneAndUpdate(find, update); + return result.getInteger("seq"); + } + + /** + * @return mongo client + */ + public MongoClient getMongoClient() { + return mongoClient; + } + + /** + * + * @return mongo database + */ + public MongoDatabase getMongoDatabase() { + return database; + } + + /** + * + * @return tickets collection + */ + public MongoCollection getTicketsCollection() { + return ticketsCollection; + } + + /** + * + * @return counters collection + */ + public MongoCollection getCountersCollection() { + return countersCollection; + } + + @Override + public Optional findById(LotteryTicketId id) { + Document find = new Document("ticketId", id.getId()); + ArrayList results = ticketsCollection.find(find).limit(1).into(new ArrayList()); + if (results.size() > 0) { + LotteryTicket lotteryTicket = docToTicket(results.get(0)); + return Optional.of(lotteryTicket); + } else { + return Optional.empty(); + } + } + + @Override + public Optional save(LotteryTicket ticket) { + int ticketId = getNextId(); + Document doc = new Document("ticketId", ticketId); + doc.put("email", ticket.getPlayerDetails().getEmail()); + doc.put("bank", ticket.getPlayerDetails().getBankAccount()); + doc.put("phone", ticket.getPlayerDetails().getPhoneNumber()); + doc.put("numbers", ticket.getNumbers().getNumbersAsString()); + ticketsCollection.insertOne(doc); + return Optional.of(new LotteryTicketId(ticketId)); + } + + @Override + public Map findAll() { + Map map = new HashMap<>(); + ArrayList docs = ticketsCollection.find(new Document()).into(new ArrayList()); + for (Document doc: docs) { + LotteryTicket lotteryTicket = docToTicket(doc); + map.put(lotteryTicket.getId(), lotteryTicket); + } + return map; + } + + @Override + public void deleteAll() { + ticketsCollection.deleteMany(new Document()); + } + + private LotteryTicket docToTicket(Document doc) { + PlayerDetails playerDetails = PlayerDetails.create(doc.getString("email"), doc.getString("bank"), + doc.getString("phone")); + int[] numArray = Arrays.asList(doc.getString("numbers").split(",")).stream().mapToInt(Integer::parseInt).toArray(); + HashSet numbers = new HashSet<>(); + for (int num: numArray) { + numbers.add(num); + } + LotteryNumbers lotteryNumbers = LotteryNumbers.create(numbers); + return LotteryTicket.create(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java index 9ce8d61bdacc..930c919da074 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -22,8 +22,10 @@ */ package com.iluwatar.hexagonal.domain; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.PrimitiveIterator; import java.util.Random; import java.util.Set; @@ -78,6 +80,22 @@ public static LotteryNumbers create(Set givenNumbers) { public Set getNumbers() { return Collections.unmodifiableSet(numbers); } + + /** + * @return numbers as comma separated string + */ + public String getNumbersAsString() { + List list = new ArrayList<>(); + list.addAll(numbers); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < NUM_NUMBERS; i++) { + builder.append(list.get(i)); + if (i < NUM_NUMBERS - 1) { + builder.append(","); + } + } + return builder.toString(); + } /** * Generates 4 unique random numbers between 1-20 into numbers set. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java index 08064f46cbbf..3e9ebdae43c6 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -29,13 +29,15 @@ */ public class LotteryTicket { + private LotteryTicketId id; private final PlayerDetails playerDetails; private final LotteryNumbers lotteryNumbers; - + /** * Constructor. */ - private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { + private LotteryTicket(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { + this.id = id; playerDetails = details; lotteryNumbers = numbers; } @@ -43,8 +45,8 @@ private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { /** * Factory for creating lottery tickets; */ - public static LotteryTicket create(PlayerDetails details, LotteryNumbers numbers) { - return new LotteryTicket(details, numbers); + public static LotteryTicket create(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { + return new LotteryTicket(id, details, numbers); } /** @@ -61,6 +63,20 @@ public LotteryNumbers getNumbers() { return lotteryNumbers; } + /** + * @return id + */ + public LotteryTicketId getId() { + return id; + } + + /** + * set id + */ + public void setId(LotteryTicketId id) { + this.id = id; + } + @Override public String toString() { return playerDetails.toString() + " " + lotteryNumbers.toString(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index c62dbfa54cd8..0a0177f25183 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -25,8 +25,8 @@ import com.google.inject.AbstractModule; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.MongoTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; @@ -36,7 +36,7 @@ public class LotteryModule extends AbstractModule { @Override protected void configure() { - bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); + bind(LotteryTicketRepository.class).to(MongoTicketRepository.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); bind(WireTransfers.class).to(InMemoryBank.class); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java index 1af18118dc27..b9c779c340c8 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java @@ -27,6 +27,7 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import java.util.ArrayList; @@ -94,7 +95,8 @@ public class SampleData { */ public static void submitTickets(LotteryService lotteryService, int numTickets) { for (int i = 0; i < numTickets; i++) { - LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); + LotteryTicket ticket = LotteryTicket.create(new LotteryTicketId(), + getRandomPlayerDetails(), LotteryNumbers.createRandom()); lotteryService.submitTicket(ticket); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index 5463eca7c447..afacc35ccda3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -83,7 +83,7 @@ public static void main(String[] args) { chosen.add(Integer.parseInt(parts[i])); } LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); - LotteryTicket lotteryTicket = LotteryTicket.create(details, lotteryNumbers); + LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); Optional id = service.submitTicket(lotteryTicket); if (id.isPresent()) { System.out.println("Submitted lottery ticket with id: " + id.get()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/InMemoryTicketRepositoryTest.java similarity index 98% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/database/InMemoryTicketRepositoryTest.java index 31cb8f5f07a2..d32e594a8f06 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/InMemoryTicketRepositoryTest.java @@ -39,7 +39,7 @@ * Tests for {@link LotteryTicketRepository} * */ -public class LotteryTicketRepositoryTest { +public class InMemoryTicketRepositoryTest { private final LotteryTicketRepository repository = new InMemoryTicketRepository(); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java new file mode 100644 index 000000000000..dc0d5c7fda87 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -0,0 +1,94 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.database; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.mongodb.MongoClient; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests for Mongo based ticket repository + */ +public class MongoTicketRepositoryTest { + + private static final String TEST_HOST = "localhost"; + private static final int TEST_PORT = 27017; + private static final String TEST_DB = "lotteryDB"; + private static final String TEST_TICKETS_COLLECTION = "lotteryTickets"; + private static final String TEST_COUNTERS_COLLECTION = "counters"; + + private MongoTicketRepository repository; + + @Before + public void init() { + MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + mongoClient.dropDatabase(TEST_DB); + mongoClient.close(); + repository = new MongoTicketRepository(TEST_HOST, TEST_PORT, TEST_DB, TEST_TICKETS_COLLECTION, + TEST_COUNTERS_COLLECTION); + } + + @Test + public void testSetup() { + assertTrue(repository.getCountersCollection().count() == 1); + assertTrue(repository.getTicketsCollection().count() == 0); + } + + @Test + public void testNextId() { + assertEquals(1, repository.getNextId()); + assertEquals(2, repository.getNextId()); + assertEquals(3, repository.getNextId()); + } + + @Test + public void testCrudOperations() { + // create new lottery ticket and save it + PlayerDetails details = PlayerDetails.create("foo@bar.com", "123-123", "07001234"); + LotteryNumbers random = LotteryNumbers.createRandom(); + LotteryTicket original = LotteryTicket.create(new LotteryTicketId(), details, random); + Optional saved = repository.save(original); + assertEquals(1, repository.getTicketsCollection().count()); + assertTrue(saved.isPresent()); + // fetch the saved lottery ticket from database and check its contents + Optional found = repository.findById(saved.get()); + assertTrue(found.isPresent()); + LotteryTicket ticket = found.get(); + assertEquals("foo@bar.com", ticket.getPlayerDetails().getEmail()); + assertEquals("123-123", ticket.getPlayerDetails().getBankAccount()); + assertEquals("07001234", ticket.getPlayerDetails().getPhoneNumber()); + assertEquals(original.getNumbers(), ticket.getNumbers()); + // clear the collection + repository.deleteAll(); + assertEquals(0, repository.getTicketsCollection().count()); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java index e1918686a26f..4840dc897771 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -36,14 +36,14 @@ public class LotteryTicketTest { public void testEquals() { PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket1 = LotteryTicket.create(details1, numbers1); + LotteryTicket ticket1 = LotteryTicket.create(new LotteryTicketId(), details1, numbers1); PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket2 = LotteryTicket.create(details2, numbers2); + LotteryTicket ticket2 = LotteryTicket.create(new LotteryTicketId(), details2, numbers2); assertEquals(ticket1, ticket2); PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); - LotteryTicket ticket3 = LotteryTicket.create(details3, numbers3); + LotteryTicket ticket3 = LotteryTicket.create(new LotteryTicketId(), details3, numbers3); assertFalse(ticket1.equals(ticket3)); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java index 883c8127f762..02304296fbe8 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; /** @@ -51,6 +52,6 @@ public static LotteryTicket createLotteryTicket(String email, String account, St Set givenNumbers) { PlayerDetails details = PlayerDetails.create(email, account, phone); LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); - return LotteryTicket.create(details, numbers); + return LotteryTicket.create(new LotteryTicketId(), details, numbers); } } From 9a90f2de1f50557e584d3a0ced4d134c8c702090 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Sun, 11 Sep 2016 18:45:51 +0100 Subject: [PATCH 173/207] Changes based on code review --- event-asynchronous/README.md | 2 + .../etc/event-asynchronous.ucls | 30 +++++---- .../com/iluwatar/event/asynchronous/App.java | 63 ++++++++++--------- .../iluwatar/event/asynchronous/Event.java | 14 ++--- .../event/asynchronous/EventManager.java | 59 ++++++++++++----- .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 37 ++++++----- 7 files changed, 125 insertions(+), 82 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index 59e6e8b335fd..dde434abae74 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -5,6 +5,8 @@ folder: event-asynchronous permalink: /patterns/event-asynchronous/ categories: Other tags: + - difficulty-intermediate + - performance - Java --- diff --git a/event-asynchronous/etc/event-asynchronous.ucls b/event-asynchronous/etc/event-asynchronous.ucls index cc7241044d56..df09fc28d955 100644 --- a/event-asynchronous/etc/event-asynchronous.ucls +++ b/event-asynchronous/etc/event-asynchronous.ucls @@ -65,12 +65,15 @@ - - - - + + + + + + + - + @@ -78,19 +81,24 @@ - + + + - - - - + + + + + + + - + diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index fa6116b46488..f951af07ca04 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -48,6 +48,8 @@ */ public class App { + public static final String PROP_FILE_NAME = "config.properties"; + boolean interactiveMode = false; /** @@ -68,15 +70,14 @@ public static void main(String[] args) { */ public void setUp() { Properties prop = new Properties(); - String propFileName = "config.properties"; - InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME); if (inputStream != null) { try { prop.load(inputStream); } catch (IOException e) { - System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); + System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -104,23 +105,23 @@ public void quickRun() { try { // Create an Asynchronous event. - int aEventId = eventManager.createAsyncEvent(60); + int aEventId = eventManager.createAsync(60); System.out.println("Event [" + aEventId + "] has been created."); - eventManager.startEvent(aEventId); + eventManager.start(aEventId); System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventId = eventManager.createSyncEvent(60); + int sEventId = eventManager.create(60); System.out.println("Event [" + sEventId + "] has been created."); - eventManager.startEvent(sEventId); + eventManager.start(sEventId); System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventId); - eventManager.getStatus(sEventId); + eventManager.status(aEventId); + eventManager.status(sEventId); - eventManager.stopEvent(aEventId); + eventManager.cancel(aEventId); System.out.println("Event [" + aEventId + "] has been stopped."); - eventManager.stopEvent(sEventId); + eventManager.cancel(sEventId); System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException @@ -136,35 +137,33 @@ public void runInteractiveMode() { EventManager eventManager = new EventManager(); Scanner s = new Scanner(System.in); - int option = 0; - option = -1; + int option = -1; while (option != 5) { - System.out - .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + System.out.println("Hello. Would you like to boil some eggs?"); + System.out.println( + "(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); System.out.print("Choose [1,2,3,4,5]: "); option = s.nextInt(); if (option == 1) { s.nextLine(); - System.out.print("(A)sync or (S)ync event?: "); + System.out.print("Boil multiple eggs at once (A) or boil them one-by-one (S)?: "); String eventType = s.nextLine(); - System.out.print("How long should this event run for (in seconds)?: "); + System.out.print("How long should this egg be boiled for (in seconds)?: "); int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventId = eventManager.createAsyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + int eventId = eventManager.createAsync(eventTime); + eventManager.start(eventId); + System.out.println("Egg [" + eventId + "] is being boiled."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventId = eventManager.createSyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + int eventId = eventManager.create(eventTime); + eventManager.start(eventId); + System.out.println("Egg [" + eventId + "] is being boiled."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -173,24 +172,26 @@ public void runInteractiveMode() { System.out.println("Unknown event type."); } } else if (option == 2) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.stopEvent(eventId); - System.out.println("Event [" + eventId + "] has been stopped."); + eventManager.cancel(eventId); + System.out.println("Egg [" + eventId + "] is removed from boiler."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.getStatus(eventId); + eventManager.status(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 4) { - eventManager.getStatusOfAllEvents(); + eventManager.statusOfAllEvents(); + } else if (option == 5) { + eventManager.shutdown(); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 4b4fe1d94412..1cb04acdc966 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -26,11 +26,10 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; private Thread thread; - private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventId, int eventTime) { + public Event(final int eventId, final int eventTime) { this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +48,9 @@ public void stop() { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] is not done."); } else { - System.out.println("[" + eventId + "] I am done."); + System.out.println("[" + eventId + "] is done."); } } @@ -61,14 +60,13 @@ public void run() { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - counter += 1; Thread.sleep(5000); // Sleep for 5 seconds. } catch (InterruptedException e) { return; } } isComplete = true; - notifyListener(); + completed(); } public final void addListener(final ThreadCompleteListener listener) { @@ -79,9 +77,9 @@ public final void removeListener(final ThreadCompleteListener listener) { this.eventListener = null; } - private final void notifyListener() { + private final void completed() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventId); + eventListener.completedEventHandler(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index d3278594fd35..e65816cec5af 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,10 +32,10 @@ */ public class EventManager implements ThreadCompleteListener { - private int minId = 1; - private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. - private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) - private int maxEventTime = 1800; // in seconds / 30 minutes. + public static final int MAX_RUNNING_EVENTS = 1000; // Just don't wanna have too many running events. :) + public static final int MIN_ID = 1; + public static final int MAX_ID = MAX_RUNNING_EVENTS; + public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; @@ -46,7 +46,7 @@ public class EventManager implements ThreadCompleteListener { */ public EventManager() { rand = new Random(1); - eventPool = new ConcurrentHashMap(maxRunningEvents); + eventPool = new ConcurrentHashMap(MAX_RUNNING_EVENTS); } @@ -59,7 +59,7 @@ public EventManager() { * @throws InvalidOperationException No new synchronous events can be created when one is already running. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createSyncEvent(int eventTime) + public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { @@ -79,18 +79,18 @@ public int createSyncEvent(int eventTime) * @throws MaxNumOfEventsAllowedException When too many events are running at a time. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - if (eventPool.size() == maxRunningEvents) { + if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } - if (eventTime >= maxEventTime) { + if (eventTime >= MAX_EVENT_TIME) { throw new LongRunningEventException( - "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again."); } int newEventId = generateId(); @@ -108,7 +108,7 @@ private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, Lo * @param eventId The event that needs to be started. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void startEvent(int eventId) throws EventDoesNotExistException { + public void start(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -122,7 +122,7 @@ public void startEvent(int eventId) throws EventDoesNotExistException { * @param eventId The event that needs to be stopped. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void stopEvent(int eventId) throws EventDoesNotExistException { + public void cancel(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -141,7 +141,7 @@ public void stopEvent(int eventId) throws EventDoesNotExistException { * @param eventId The event to inquire status of. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void getStatus(int eventId) throws EventDoesNotExistException { + public void status(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -153,7 +153,7 @@ public void getStatus(int eventId) throws EventDoesNotExistException { * Gets status of all running events. */ @SuppressWarnings("rawtypes") - public void getStatusOfAllEvents() { + public void statusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); @@ -161,6 +161,18 @@ public void getStatusOfAllEvents() { } } + /** + * Stop all running events. + */ + @SuppressWarnings("rawtypes") + public void shutdown() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).stop(); + } + } + /** * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most * Integer.MAX_VALUE - 1. @@ -168,9 +180,9 @@ public void getStatusOfAllEvents() { private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxId - minId) + 1) + minId; + int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxId - minId) + 1) + minId; + randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; } return randomNum; @@ -180,9 +192,22 @@ private int generateId() { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventId) { + public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); eventPool.remove(eventId); } + /** + * Getter method for event pool. + */ + public Map getEventPool() { + return eventPool; + } + + /** + * Get number of currently running Synchronous events. + */ + public int numOfCurrentlyRunningSyncEvent() { + return currentlyRunningSyncEvent; + } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index 88f300634915..fd62a3e8053d 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventId); + void completedEventHandler(final int eventId); } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 392c7fba6ccd..6565d5badbc4 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -16,6 +16,8 @@ */ package com.iluwatar.event.asynchronous; +import static org.junit.Assert.assertTrue; + import org.junit.Before; import org.junit.Test; @@ -36,9 +38,13 @@ public void setUp() { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventId = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventId); - eventManager.stopEvent(aEventId); + int aEventId = eventManager.createAsync(60); + eventManager.start(aEventId); + assertTrue(eventManager.getEventPool().size() == 1); + assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); + assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() == -1); + eventManager.cancel(aEventId); + assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,25 +54,28 @@ public void testAsynchronousEvent() { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - eventManager.stopEvent(sEventId); + int sEventId = eventManager.create(60); + eventManager.start(sEventId); + assertTrue(eventManager.getEventPool().size() == 1); + assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); + assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() != -1); + eventManager.cancel(sEventId); + assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); } } - @Test - public void testUnsuccessfulSynchronousEvent() { + @Test(expected = InvalidOperationException.class) + public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException - | InvalidOperationException e) { + int sEventId = eventManager.create(60); + eventManager.start(sEventId); + sEventId = eventManager.create(60); + eventManager.start(sEventId); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } From 59e6a0af853c8064735302bf8eae6847bb54f6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 22:16:50 +0300 Subject: [PATCH 174/207] Hexagonal pattern: Ignore Mongo repository test --- .../iluwatar/hexagonal/database/MongoTicketRepositoryTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index dc0d5c7fda87..09a2772bddd4 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.domain.PlayerDetails; import com.mongodb.MongoClient; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.Optional; @@ -38,6 +39,7 @@ /** * Tests for Mongo based ticket repository */ +@Ignore public class MongoTicketRepositoryTest { private static final String TEST_HOST = "localhost"; From e685512ed59a46f02e3b7670b8ee6731fc3279e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 23:19:02 +0300 Subject: [PATCH 175/207] Hexagonal pattern: Added Mongo based banking adapter and bound it in Guice production module --- .../iluwatar/hexagonal/banking/MongoBank.java | 134 ++++++++++++++++++ .../hexagonal/module/LotteryModule.java | 4 +- ...ansfersTest.java => InMemoryBankTest.java} | 2 +- .../hexagonal/banking/MongoBankTest.java | 68 +++++++++ .../database/MongoTicketRepositoryTest.java | 6 +- 5 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/banking/{WireTransfersTest.java => InMemoryBankTest.java} (98%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java new file mode 100644 index 000000000000..3da65e156b71 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java @@ -0,0 +1,134 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.banking; + +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.UpdateOptions; +import org.bson.Document; + +import java.util.ArrayList; + +/** + * Mongo based banking adapter + */ +public class MongoBank implements WireTransfers { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 27017; + private static final String DEFAULT_DB = "lotteryDB"; + private static final String DEFAULT_ACCOUNTS_COLLECTION = "accounts"; + + private MongoClient mongoClient; + private MongoDatabase database; + private MongoCollection accountsCollection; + + /** + * Constructor + */ + public MongoBank() { + connect(); + } + + /** + * Constructor accepting parameters + */ + public MongoBank(String host, int port, String dbName, String accountsCollectionName) { + connect(host, port, dbName, accountsCollectionName); + } + + /** + * Connect to database with default parameters + */ + public void connect() { + connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); + } + + /** + * Connect to database with given parameters + */ + public void connect(String host, int port, String dbName, String accountsCollectionName) { + if (mongoClient != null) { + mongoClient.close(); + } + mongoClient = new MongoClient(host , port); + database = mongoClient.getDatabase(dbName); + accountsCollection = database.getCollection(accountsCollectionName); + } + + /** + * @return mongo client + */ + public MongoClient getMongoClient() { + return mongoClient; + } + + /** + * + * @return mongo database + */ + public MongoDatabase getMongoDatabase() { + return database; + } + + /** + * + * @return accounts collection + */ + public MongoCollection getAccountsCollection() { + return accountsCollection; + } + + + @Override + public void setFunds(String bankAccount, int amount) { + Document search = new Document("_id", bankAccount); + Document update = new Document("_id", bankAccount).append("funds", amount); + accountsCollection.updateOne(search, new Document("$set", update), new UpdateOptions().upsert(true)); + } + + @Override + public int getFunds(String bankAccount) { + Document search = new Document("_id", bankAccount); + ArrayList results = accountsCollection.find(search).limit(1).into(new ArrayList()); + if (results.size() > 0) { + return results.get(0).getInteger("funds"); + } else { + return 0; + } + } + + @Override + public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { + int sourceFunds = getFunds(sourceBackAccount); + if (sourceFunds < amount) { + return false; + } else { + int destFunds = getFunds(destinationBankAccount); + setFunds(sourceBackAccount, sourceFunds - amount); + setFunds(destinationBankAccount, destFunds + amount); + return true; + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index 0a0177f25183..c9bc301fc025 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -23,7 +23,7 @@ package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; -import com.iluwatar.hexagonal.banking.InMemoryBank; +import com.iluwatar.hexagonal.banking.MongoBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.MongoTicketRepository; @@ -38,6 +38,6 @@ public class LotteryModule extends AbstractModule { protected void configure() { bind(LotteryTicketRepository.class).to(MongoTicketRepository.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); - bind(WireTransfers.class).to(InMemoryBank.class); + bind(WireTransfers.class).to(MongoBank.class); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/InMemoryBankTest.java similarity index 98% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/banking/InMemoryBankTest.java index 25fbf460c3a4..c5efda24046a 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/InMemoryBankTest.java @@ -32,7 +32,7 @@ * Tests for banking * */ -public class WireTransfersTest { +public class InMemoryBankTest { private final WireTransfers bank = new InMemoryBank(); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java new file mode 100644 index 000000000000..ad142f02811e --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.banking; + +import com.mongodb.MongoClient; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for Mongo banking adapter + */ +@Ignore +public class MongoBankTest { + + private static final String TEST_HOST = "localhost"; + private static final int TEST_PORT = 27017; + private static final String TEST_DB = "lotteryDBTest"; + private static final String TEST_ACCOUNTS_COLLECTION = "testAccounts"; + + private MongoBank mongoBank; + + @Before + public void init() { + MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + mongoClient.dropDatabase(TEST_DB); + mongoClient.close(); + mongoBank = new MongoBank(TEST_HOST, TEST_PORT, TEST_DB, TEST_ACCOUNTS_COLLECTION); + } + + @Test + public void testSetup() { + assertEquals(0, mongoBank.getAccountsCollection().count()); + } + + @Test + public void testFundTransfers() { + assertEquals(0, mongoBank.getFunds("000-000")); + mongoBank.setFunds("000-000", 10); + assertEquals(10, mongoBank.getFunds("000-000")); + assertEquals(0, mongoBank.getFunds("111-111")); + mongoBank.transferFunds(9, "000-000", "111-111"); + assertEquals(1, mongoBank.getFunds("000-000")); + assertEquals(9, mongoBank.getFunds("111-111")); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index 09a2772bddd4..45fa1cc67bed 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -44,9 +44,9 @@ public class MongoTicketRepositoryTest { private static final String TEST_HOST = "localhost"; private static final int TEST_PORT = 27017; - private static final String TEST_DB = "lotteryDB"; - private static final String TEST_TICKETS_COLLECTION = "lotteryTickets"; - private static final String TEST_COUNTERS_COLLECTION = "counters"; + private static final String TEST_DB = "lotteryTestDB"; + private static final String TEST_TICKETS_COLLECTION = "lotteryTestTickets"; + private static final String TEST_COUNTERS_COLLECTION = "testCounters"; private MongoTicketRepository repository; From 27e8cb7f2d28bcc09c3cd0babe8ed901e1bd3804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 14 Sep 2016 19:25:26 +0300 Subject: [PATCH 176/207] Hexagonal pattern: Add separate class for Mongo connection properties --- .../iluwatar/hexagonal/banking/MongoBank.java | 14 ++-- .../database/MongoTicketRepository.java | 14 ++-- .../mongo/MongoConnectionProperties.java | 80 +++++++++++++++++++ .../hexagonal/banking/MongoBankTest.java | 8 +- .../database/MongoTicketRepositoryTest.java | 8 +- 5 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java index 3da65e156b71..2a18d8986b12 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java @@ -22,6 +22,7 @@ */ package com.iluwatar.hexagonal.banking; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -35,8 +36,6 @@ */ public class MongoBank implements WireTransfers { - private static final String DEFAULT_HOST = "localhost"; - private static final int DEFAULT_PORT = 27017; private static final String DEFAULT_DB = "lotteryDB"; private static final String DEFAULT_ACCOUNTS_COLLECTION = "accounts"; @@ -54,25 +53,26 @@ public MongoBank() { /** * Constructor accepting parameters */ - public MongoBank(String host, int port, String dbName, String accountsCollectionName) { - connect(host, port, dbName, accountsCollectionName); + public MongoBank(String dbName, String accountsCollectionName) { + connect(dbName, accountsCollectionName); } /** * Connect to database with default parameters */ public void connect() { - connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); + connect(DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); } /** * Connect to database with given parameters */ - public void connect(String host, int port, String dbName, String accountsCollectionName) { + public void connect(String dbName, String accountsCollectionName) { if (mongoClient != null) { mongoClient.close(); } - mongoClient = new MongoClient(host , port); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + mongoClient = new MongoClient(properties.getHost(), properties.getPort()); database = mongoClient.getDatabase(dbName); accountsCollection = database.getCollection(accountsCollectionName); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java index ff0439af886c..73ff40b78265 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -26,6 +26,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -43,8 +44,6 @@ */ public class MongoTicketRepository implements LotteryTicketRepository { - private static final String DEFAULT_HOST = "localhost"; - private static final int DEFAULT_PORT = 27017; private static final String DEFAULT_DB = "lotteryDB"; private static final String DEFAULT_TICKETS_COLLECTION = "lotteryTickets"; private static final String DEFAULT_COUNTERS_COLLECTION = "counters"; @@ -64,27 +63,28 @@ public MongoTicketRepository() { /** * Constructor accepting parameters */ - public MongoTicketRepository(String host, int port, String dbName, String ticketsCollectionName, + public MongoTicketRepository(String dbName, String ticketsCollectionName, String countersCollectionName) { - connect(host, port, dbName, ticketsCollectionName, countersCollectionName); + connect(dbName, ticketsCollectionName, countersCollectionName); } /** * Connect to database with default parameters */ public void connect() { - connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); + connect(DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); } /** * Connect to database with given parameters */ - public void connect(String host, int port, String dbName, String ticketsCollectionName, + public void connect(String dbName, String ticketsCollectionName, String countersCollectionName) { if (mongoClient != null) { mongoClient.close(); } - mongoClient = new MongoClient(host , port); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + mongoClient = new MongoClient(properties.getHost(), properties.getPort()); database = mongoClient.getDatabase(dbName); ticketsCollection = database.getCollection(ticketsCollectionName); countersCollection = database.getCollection(countersCollectionName); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java new file mode 100644 index 000000000000..20b1876f670e --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java @@ -0,0 +1,80 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.mongo; + +import java.io.FileInputStream; +import java.util.Properties; + +/** + * Mongo connection properties + */ +public class MongoConnectionProperties { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 27017; + + private String host; + private int port; + + /** + * Constructor + */ + public MongoConnectionProperties() { + this.host = DEFAULT_HOST; + this.port = DEFAULT_PORT; + } + + /** + * @return host name + */ + public String getHost() { + return host; + } + + /** + * @return port number + */ + public int getPort() { + return port; + } + + /** + * Try to load connection properties from file. + * Fall back to default connection properties. + */ + public MongoConnectionProperties load() { + String path = System.getProperty("hexagonal.properties.path"); + Properties properties = new Properties(); + if (path != null) { + try (FileInputStream fin = new FileInputStream(path)) { + properties.load(fin); + this.host = properties.getProperty("host"); + this.port = Integer.parseInt(properties.getProperty("port")); + } catch (Exception e) { + // error occurred, use default properties + e.printStackTrace(); + } + } + return this; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java index ad142f02811e..26041b174aed 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java @@ -22,6 +22,7 @@ */ package com.iluwatar.hexagonal.banking; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -35,8 +36,6 @@ @Ignore public class MongoBankTest { - private static final String TEST_HOST = "localhost"; - private static final int TEST_PORT = 27017; private static final String TEST_DB = "lotteryDBTest"; private static final String TEST_ACCOUNTS_COLLECTION = "testAccounts"; @@ -44,10 +43,11 @@ public class MongoBankTest { @Before public void init() { - MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); - mongoBank = new MongoBank(TEST_HOST, TEST_PORT, TEST_DB, TEST_ACCOUNTS_COLLECTION); + mongoBank = new MongoBank(TEST_DB, TEST_ACCOUNTS_COLLECTION); } @Test diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index 45fa1cc67bed..a29b535f602b 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -26,6 +26,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -42,8 +43,6 @@ @Ignore public class MongoTicketRepositoryTest { - private static final String TEST_HOST = "localhost"; - private static final int TEST_PORT = 27017; private static final String TEST_DB = "lotteryTestDB"; private static final String TEST_TICKETS_COLLECTION = "lotteryTestTickets"; private static final String TEST_COUNTERS_COLLECTION = "testCounters"; @@ -52,10 +51,11 @@ public class MongoTicketRepositoryTest { @Before public void init() { - MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); - repository = new MongoTicketRepository(TEST_HOST, TEST_PORT, TEST_DB, TEST_TICKETS_COLLECTION, + repository = new MongoTicketRepository(TEST_DB, TEST_TICKETS_COLLECTION, TEST_COUNTERS_COLLECTION); } From 3cf2b34d2a1f20a2606d515ad145bf0fb018b44f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 14 Sep 2016 22:01:41 +0300 Subject: [PATCH 177/207] Hexagonal pattern: Improve connection properties handling --- .../administration/ConsoleAdministration.java | 2 + .../iluwatar/hexagonal/banking/MongoBank.java | 5 +-- .../database/MongoTicketRepository.java | 5 +-- ...a => MongoConnectionPropertiesLoader.java} | 40 +++++-------------- .../hexagonal/service/ConsoleLottery.java | 2 + .../hexagonal/banking/MongoBankTest.java | 7 ++-- .../database/MongoTicketRepositoryTest.java | 7 ++-- 7 files changed, 25 insertions(+), 43 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/{MongoConnectionProperties.java => MongoConnectionPropertiesLoader.java} (75%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index 6a846280c4f0..ea2f33699416 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.iluwatar.hexagonal.sampledata.SampleData; import java.util.Scanner; @@ -41,6 +42,7 @@ public class ConsoleAdministration { * Program entry point */ public static void main(String[] args) { + MongoConnectionPropertiesLoader.load(); Injector injector = Guice.createInjector(new LotteryModule()); LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); LotteryService service = injector.getInstance(LotteryService.class); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java index 2a18d8986b12..23a0e376a724 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java @@ -22,7 +22,6 @@ */ package com.iluwatar.hexagonal.banking; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -71,8 +70,8 @@ public void connect(String dbName, String accountsCollectionName) { if (mongoClient != null) { mongoClient.close(); } - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); database = mongoClient.getDatabase(dbName); accountsCollection = database.getCollection(accountsCollectionName); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java index 73ff40b78265..4cfa8364912b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -26,7 +26,6 @@ import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -83,8 +82,8 @@ public void connect(String dbName, String ticketsCollectionName, if (mongoClient != null) { mongoClient.close(); } - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); database = mongoClient.getDatabase(dbName); ticketsCollection = database.getCollection(ticketsCollectionName); countersCollection = database.getCollection(countersCollectionName); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionPropertiesLoader.java similarity index 75% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionPropertiesLoader.java index 20b1876f670e..a9bb39803809 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionPropertiesLoader.java @@ -26,55 +26,33 @@ import java.util.Properties; /** - * Mongo connection properties + * Mongo connection properties loader */ -public class MongoConnectionProperties { +public class MongoConnectionPropertiesLoader { private static final String DEFAULT_HOST = "localhost"; private static final int DEFAULT_PORT = 27017; - private String host; - private int port; - - /** - * Constructor - */ - public MongoConnectionProperties() { - this.host = DEFAULT_HOST; - this.port = DEFAULT_PORT; - } - - /** - * @return host name - */ - public String getHost() { - return host; - } - - /** - * @return port number - */ - public int getPort() { - return port; - } - /** * Try to load connection properties from file. * Fall back to default connection properties. */ - public MongoConnectionProperties load() { + public static void load() { + String host = DEFAULT_HOST; + int port = DEFAULT_PORT; String path = System.getProperty("hexagonal.properties.path"); Properties properties = new Properties(); if (path != null) { try (FileInputStream fin = new FileInputStream(path)) { properties.load(fin); - this.host = properties.getProperty("host"); - this.port = Integer.parseInt(properties.getProperty("port")); + host = properties.getProperty("mongo-host"); + port = Integer.parseInt(properties.getProperty("mongo-port")); } catch (Exception e) { // error occurred, use default properties e.printStackTrace(); } } - return this; + System.setProperty("mongo-host", host); + System.setProperty("mongo-port", String.format("%d", port)); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index afacc35ccda3..b0d660023864 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -32,6 +32,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import java.util.HashSet; import java.util.Optional; @@ -48,6 +49,7 @@ public class ConsoleLottery { * Program entry point */ public static void main(String[] args) { + MongoConnectionPropertiesLoader.load(); Injector injector = Guice.createInjector(new LotteryModule()); LotteryService service = injector.getInstance(LotteryService.class); WireTransfers bank = injector.getInstance(WireTransfers.class); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java index 26041b174aed..ce5c9ff1aeef 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java @@ -22,7 +22,7 @@ */ package com.iluwatar.hexagonal.banking; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -43,8 +43,9 @@ public class MongoBankTest { @Before public void init() { - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + MongoConnectionPropertiesLoader.load(); + MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); mongoBank = new MongoBank(TEST_DB, TEST_ACCOUNTS_COLLECTION); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index a29b535f602b..bbd95b38ddf1 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -26,7 +26,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -51,8 +51,9 @@ public class MongoTicketRepositoryTest { @Before public void init() { - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + MongoConnectionPropertiesLoader.load(); + MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); repository = new MongoTicketRepository(TEST_DB, TEST_TICKETS_COLLECTION, From c4c5e78e50f6ccfe73e5fafaf4019cfb3dc68fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 14 Sep 2016 22:18:42 +0300 Subject: [PATCH 178/207] Hexagonal pattern: Improve error handling in console lottery --- .../hexagonal/service/ConsoleLottery.java | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index b0d660023864..eb791ca552b7 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -79,17 +79,21 @@ public static void main(String[] args) { PlayerDetails details = PlayerDetails.create(email, account, phone); System.out.println("Give 4 comma separated lottery numbers?"); String numbers = readString(scanner); - String[] parts = numbers.split(","); - Set chosen = new HashSet<>(); - for (int i = 0; i < 4; i++) { - chosen.add(Integer.parseInt(parts[i])); - } - LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); - LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); - Optional id = service.submitTicket(lotteryTicket); - if (id.isPresent()) { - System.out.println("Submitted lottery ticket with id: " + id.get()); - } else { + try { + String[] parts = numbers.split(","); + Set chosen = new HashSet<>(); + for (int i = 0; i < 4; i++) { + chosen.add(Integer.parseInt(parts[i])); + } + LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); + LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); + Optional id = service.submitTicket(lotteryTicket); + if (id.isPresent()) { + System.out.println("Submitted lottery ticket with id: " + id.get()); + } else { + System.out.println("Failed submitting lottery ticket - please try again."); + } + } catch (Exception e) { System.out.println("Failed submitting lottery ticket - please try again."); } } else if (cmd.equals("4")) { @@ -97,19 +101,23 @@ public static void main(String[] args) { String id = readString(scanner); System.out.println("Give the 4 comma separated winning numbers?"); String numbers = readString(scanner); - String[] parts = numbers.split(","); - Set winningNumbers = new HashSet<>(); - for (int i = 0; i < 4; i++) { - winningNumbers.add(Integer.parseInt(parts[i])); - } - LotteryTicketCheckResult result = service.checkTicketForPrize( - new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); - if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - System.out.println("Congratulations! The lottery ticket has won!"); - } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - System.out.println("Unfortunately the lottery ticket did not win."); - } else { - System.out.println("Such lottery ticket has not been submitted."); + try { + String[] parts = numbers.split(","); + Set winningNumbers = new HashSet<>(); + for (int i = 0; i < 4; i++) { + winningNumbers.add(Integer.parseInt(parts[i])); + } + LotteryTicketCheckResult result = service.checkTicketForPrize( + new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + System.out.println("Congratulations! The lottery ticket has won!"); + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + System.out.println("Unfortunately the lottery ticket did not win."); + } else { + System.out.println("Such lottery ticket has not been submitted."); + } + } catch (Exception e) { + System.out.println("Failed checking the lottery ticket - please try again."); } } else if (cmd.equals("5")) { exit = true; From df32a7b893fb0120708bfde3d7c0122264033f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 15 Sep 2016 21:45:09 +0300 Subject: [PATCH 179/207] Hexagonal pattern: Introduced lottery events port with two adapters --- hexagonal/etc/hexagonal.ucls | 4 +- hexagonal/etc/hexagonal.urm.puml | 2 +- .../main/java/com/iluwatar/hexagonal/App.java | 2 +- .../domain/LotteryAdministration.java | 12 +- .../hexagonal/domain/LotteryService.java | 10 +- .../LotteryEventLog.java} | 26 +-- .../hexagonal/eventlog/MongoEventLog.java | 154 ++++++++++++++++++ .../StdOutEventLog.java} | 17 +- .../hexagonal/module/LotteryModule.java | 6 +- .../module/LotteryTestingModule.java | 6 +- .../hexagonal/eventlog/MongoEventLogTest.java | 84 ++++++++++ 11 files changed, 282 insertions(+), 41 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{notifications/LotteryNotifications.java => eventlog/LotteryEventLog.java} (66%) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java rename hexagonal/src/main/java/com/iluwatar/hexagonal/{notifications/StdOutNotifications.java => eventlog/StdOutEventLog.java} (83%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls index 0318576c2c27..d3b46b23d08e 100644 --- a/hexagonal/etc/hexagonal.ucls +++ b/hexagonal/etc/hexagonal.ucls @@ -2,7 +2,7 @@ - @@ -161,7 +161,7 @@ - diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml index 4102c5863b81..c4b0d0a73412 100644 --- a/hexagonal/etc/hexagonal.urm.puml +++ b/hexagonal/etc/hexagonal.urm.puml @@ -116,7 +116,7 @@ package com.iluwatar.hexagonal.database { + save(LotteryTicket) : Optional {abstract} } } -package com.iluwatar.hexagonal.notifications { +package com.iluwatar.hexagonal.eventlog { interface LotteryNotifications { + notifyNoWin(PlayerDetails) {abstract} + notifyPrize(PlayerDetails, int) {abstract} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index a7d31446bb48..7ec974c3b029 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -56,7 +56,7 @@ * * The secondary ports that application core uses are {@link WireTransfers} * which is a banking service, {@link LotteryNotifications} that delivers - * notifications as lottery events occur and {@link LotteryTicketRepository} + * eventlog as lottery events occur and {@link LotteryTicketRepository} * that is the storage for the lottery tickets. * */ diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index f7339086342d..3e01b6e03c55 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -25,7 +25,7 @@ import com.google.inject.Inject; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; import java.util.Map; @@ -37,7 +37,7 @@ public class LotteryAdministration { private final LotteryTicketRepository repository; - private final LotteryNotifications notifications; + private final LotteryEventLog notifications; private final WireTransfers wireTransfers; private final LotteryTicketChecker checker; @@ -45,7 +45,7 @@ public class LotteryAdministration { * Constructor */ @Inject - public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications, + public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications, WireTransfers wireTransfers) { this.repository = repository; this.notifications = notifications; @@ -72,12 +72,12 @@ public LotteryNumbers performLottery() { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); if (transferred) { - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + notifications.ticketWon(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); } else { - notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + notifications.prizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); } } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + notifications.ticketDidNotWin(tickets.get(id).getPlayerDetails()); } } return numbers; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 76cd47d1dcb4..a9dff7fd0f41 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -25,7 +25,7 @@ import com.google.inject.Inject; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; import java.util.Optional; @@ -37,7 +37,7 @@ public class LotteryService { private final LotteryTicketRepository repository; - private final LotteryNotifications notifications; + private final LotteryEventLog notifications; private final WireTransfers wireTransfers; private final LotteryTicketChecker checker; @@ -45,7 +45,7 @@ public class LotteryService { * Constructor */ @Inject - public LotteryService(LotteryTicketRepository repository, LotteryNotifications notifications, + public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications, WireTransfers wireTransfers) { this.repository = repository; this.notifications = notifications; @@ -60,12 +60,12 @@ public Optional submitTicket(LotteryTicket ticket) { boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + notifications.ticketSubmitError(ticket.getPlayerDetails()); return Optional.empty(); } Optional optional = repository.save(ticket); if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + notifications.ticketSubmitted(ticket.getPlayerDetails()); } return optional; } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java similarity index 66% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java index d7a0cc870348..e47640e27baa 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java @@ -20,40 +20,40 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.notifications; +package com.iluwatar.hexagonal.eventlog; import com.iluwatar.hexagonal.domain.PlayerDetails; /** * - * Provides notifications for lottery events. + * Event log for lottery events * */ -public interface LotteryNotifications { +public interface LotteryEventLog { /** - * Notify lottery ticket was submitted + * lottery ticket submitted */ - void notifyTicketSubmitted(PlayerDetails details); + void ticketSubmitted(PlayerDetails details); /** - * Notify there was an error submitting lottery ticket + * error submitting lottery ticket */ - void notifyTicketSubmitError(PlayerDetails details); + void ticketSubmitError(PlayerDetails details); /** - * Notify lottery ticket did not win + * lottery ticket did not win */ - void notifyNoWin(PlayerDetails details); + void ticketDidNotWin(PlayerDetails details); /** - * Notify that prize has been paid + * lottery ticket won */ - void notifyPrize(PlayerDetails details, int prizeAmount); + void ticketWon(PlayerDetails details, int prizeAmount); /** - * Notify that there was an error paying the prize + * error paying the prize */ - void notifyPrizeError(PlayerDetails details, int prizeAmount); + void prizeError(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java new file mode 100644 index 000000000000..7f9b4da5e61d --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java @@ -0,0 +1,154 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.eventlog; + +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.bson.Document; + +/** + * Mongo based event log + */ +public class MongoEventLog implements LotteryEventLog { + + private static final String DEFAULT_DB = "lotteryDB"; + private static final String DEFAULT_EVENTS_COLLECTION = "events"; + + private MongoClient mongoClient; + private MongoDatabase database; + private MongoCollection eventsCollection; + + private StdOutEventLog stdOutEventLog = new StdOutEventLog(); + + /** + * Constructor + */ + public MongoEventLog() { + connect(); + } + + /** + * Constructor accepting parameters + */ + public MongoEventLog(String dbName, String eventsCollectionName) { + connect(dbName, eventsCollectionName); + } + + /** + * Connect to database with default parameters + */ + public void connect() { + connect(DEFAULT_DB, DEFAULT_EVENTS_COLLECTION); + } + + /** + * Connect to database with given parameters + */ + public void connect(String dbName, String eventsCollectionName) { + if (mongoClient != null) { + mongoClient.close(); + } + mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); + database = mongoClient.getDatabase(dbName); + eventsCollection = database.getCollection(eventsCollectionName); + } + + /** + * @return mongo client + */ + public MongoClient getMongoClient() { + return mongoClient; + } + + /** + * + * @return mongo database + */ + public MongoDatabase getMongoDatabase() { + return database; + } + + /** + * + * @return accounts collection + */ + public MongoCollection getEventsCollection() { + return eventsCollection; + } + + + @Override + public void ticketSubmitted(PlayerDetails details) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket was submitted and bank account was charged for 3 credits.")); + eventsCollection.insertOne(document); + stdOutEventLog.ticketSubmitted(details); + } + + @Override + public void ticketSubmitError(PlayerDetails details) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket could not be submitted because lack of funds.")); + eventsCollection.insertOne(document); + stdOutEventLog.ticketSubmitError(details); + } + + @Override + public void ticketDidNotWin(PlayerDetails details) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket was checked and unfortunately did not win this time.")); + eventsCollection.insertOne(document); + stdOutEventLog.ticketDidNotWin(details); + } + + @Override + public void ticketWon(PlayerDetails details, int prizeAmount) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket won! The bank account was deposited with %d credits.", + prizeAmount)); + eventsCollection.insertOne(document); + stdOutEventLog.ticketWon(details, prizeAmount); + } + + @Override + public void prizeError(PlayerDetails details, int prizeAmount) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.", + prizeAmount)); + eventsCollection.insertOne(document); + stdOutEventLog.prizeError(details, prizeAmount); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java similarity index 83% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java index f6bd3b546259..4150dd4011f8 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java @@ -20,40 +20,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.notifications; +package com.iluwatar.hexagonal.eventlog; import com.iluwatar.hexagonal.domain.PlayerDetails; -public class StdOutNotifications implements LotteryNotifications { +/** + * Standard output event log + */ +public class StdOutEventLog implements LotteryEventLog { @Override - public void notifyTicketSubmitted(PlayerDetails details) { + public void ticketSubmitted(PlayerDetails details) { System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.", details.getEmail(), details.getBankAccount())); } @Override - public void notifyNoWin(PlayerDetails details) { + public void ticketDidNotWin(PlayerDetails details) { System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.", details.getEmail())); } @Override - public void notifyPrize(PlayerDetails details, int prizeAmount) { + public void ticketWon(PlayerDetails details, int prizeAmount) { System.out .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", details.getEmail(), details.getBankAccount(), prizeAmount)); } @Override - public void notifyPrizeError(PlayerDetails details, int prizeAmount) { + public void prizeError(PlayerDetails details, int prizeAmount) { System.out .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", details.getEmail(), prizeAmount)); } @Override - public void notifyTicketSubmitError(PlayerDetails details) { + public void ticketSubmitError(PlayerDetails details) { System.out.println( String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", details.getEmail())); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index c9bc301fc025..7e784548d7ce 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -27,8 +27,8 @@ import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.MongoTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.StdOutNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; +import com.iluwatar.hexagonal.eventlog.MongoEventLog; /** * Guice module for binding production dependencies @@ -37,7 +37,7 @@ public class LotteryModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(MongoTicketRepository.class); - bind(LotteryNotifications.class).to(StdOutNotifications.class); + bind(LotteryEventLog.class).to(MongoEventLog.class); bind(WireTransfers.class).to(MongoBank.class); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java index c934ed43cdcc..2a1ad1155bf6 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java @@ -27,8 +27,8 @@ import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.StdOutNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; +import com.iluwatar.hexagonal.eventlog.StdOutEventLog; /** * Guice module for testing dependencies @@ -37,7 +37,7 @@ public class LotteryTestingModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); - bind(LotteryNotifications.class).to(StdOutNotifications.class); + bind(LotteryEventLog.class).to(StdOutEventLog.class); bind(WireTransfers.class).to(InMemoryBank.class); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java new file mode 100644 index 000000000000..9b9f14c78253 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.eventlog; + +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; +import com.mongodb.MongoClient; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for Mongo event log + */ +@Ignore +public class MongoEventLogTest { + + private static final String TEST_DB = "lotteryDBTest"; + private static final String TEST_EVENTS_COLLECTION = "testEvents"; + + private MongoEventLog mongoEventLog; + + @Before + public void init() { + MongoConnectionPropertiesLoader.load(); + MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); + mongoClient.dropDatabase(TEST_DB); + mongoClient.close(); + mongoEventLog = new MongoEventLog(TEST_DB, TEST_EVENTS_COLLECTION); + } + + @Test + public void testSetup() { + assertEquals(0, mongoEventLog.getEventsCollection().count()); + } + + @Test + public void testFundTransfers() { + PlayerDetails playerDetails = PlayerDetails.create("john@wayne.com", "000-000", "03432534543"); + mongoEventLog.prizeError(playerDetails, 1000); + assertEquals(1, mongoEventLog.getEventsCollection().count()); + mongoEventLog.prizeError(playerDetails, 1000); + assertEquals(2, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketDidNotWin(playerDetails); + assertEquals(3, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketDidNotWin(playerDetails); + assertEquals(4, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitError(playerDetails); + assertEquals(5, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitError(playerDetails); + assertEquals(6, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitted(playerDetails); + assertEquals(7, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitted(playerDetails); + assertEquals(8, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketWon(playerDetails, 1000); + assertEquals(9, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketWon(playerDetails, 1000); + assertEquals(10, mongoEventLog.getEventsCollection().count()); + } +} From 914d1353a18fbbec8c7036a131f64638da7ebeb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 15 Sep 2016 21:56:15 +0300 Subject: [PATCH 180/207] Hexagonal pattern: Update test application description --- .../src/main/java/com/iluwatar/hexagonal/App.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 7ec974c3b029..97190937c460 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -49,13 +49,13 @@ * The application core is separate from the services that drive it and * from the services it uses.

    * - * The primary ports for the application are {@link LotteryAdministration} - * through which the lottery round is initiated and run and - * {@link LotteryService} that allows players to submit lottery tickets for - * the draw.

    + * The primary ports for the application are console interfaces + * {@link ConsoleAdministration} through which the lottery round is + * initiated and run and {@link ConsoleLottery} that allows players to + * submit lottery tickets for the draw.

    * * The secondary ports that application core uses are {@link WireTransfers} - * which is a banking service, {@link LotteryNotifications} that delivers + * which is a banking service, {@link LotteryEventLog} that delivers * eventlog as lottery events occur and {@link LotteryTicketRepository} * that is the storage for the lottery tickets. * From 6aa58e8ae658cfc66e4421e9b8c92bb017b46906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 17 Sep 2016 09:07:06 +0300 Subject: [PATCH 181/207] Hexagonal pattern: Remove unnecessary factories --- .../database/MongoTicketRepository.java | 4 +- .../domain/LotteryAdministration.java | 4 +- .../hexagonal/domain/LotteryConstants.java | 3 + .../hexagonal/domain/LotteryService.java | 4 +- .../hexagonal/domain/LotteryTicket.java | 9 +-- .../hexagonal/domain/PlayerDetails.java | 11 +-- .../hexagonal/sampledata/SampleData.java | 80 +++++++++---------- .../hexagonal/service/ConsoleLottery.java | 4 +- .../database/MongoTicketRepositoryTest.java | 4 +- .../hexagonal/domain/LotteryTicketTest.java | 12 +-- .../hexagonal/domain/PlayerDetailsTest.java | 6 +- .../hexagonal/eventlog/MongoEventLogTest.java | 2 +- .../hexagonal/test/LotteryTestUtils.java | 4 +- 13 files changed, 66 insertions(+), 81 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java index 4cfa8364912b..13a937a1bc78 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -180,7 +180,7 @@ public void deleteAll() { } private LotteryTicket docToTicket(Document doc) { - PlayerDetails playerDetails = PlayerDetails.create(doc.getString("email"), doc.getString("bank"), + PlayerDetails playerDetails = new PlayerDetails(doc.getString("email"), doc.getString("bank"), doc.getString("phone")); int[] numArray = Arrays.asList(doc.getString("numbers").split(",")).stream().mapToInt(Integer::parseInt).toArray(); HashSet numbers = new HashSet<>(); @@ -188,6 +188,6 @@ private LotteryTicket docToTicket(Document doc) { numbers.add(num); } LotteryNumbers lotteryNumbers = LotteryNumbers.create(numbers); - return LotteryTicket.create(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); + return new LotteryTicket(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 3e01b6e03c55..d544c84a1991 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -39,7 +39,6 @@ public class LotteryAdministration { private final LotteryTicketRepository repository; private final LotteryEventLog notifications; private final WireTransfers wireTransfers; - private final LotteryTicketChecker checker; /** * Constructor @@ -50,7 +49,6 @@ public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog this.repository = repository; this.notifications = notifications; this.wireTransfers = wireTransfers; - this.checker = new LotteryTicketChecker(this.repository); } /** @@ -67,7 +65,7 @@ public LotteryNumbers performLottery() { LotteryNumbers numbers = LotteryNumbers.createRandom(); Map tickets = getAllSubmittedTickets(); for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = checker.checkTicketForPrize(id, numbers); + LotteryTicketCheckResult result = new LotteryTicketChecker(repository).checkTicketForPrize(id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java index fb4c8025ff89..28fd4e2f49fc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java @@ -29,6 +29,9 @@ */ public class LotteryConstants { + private LotteryConstants() { + } + public static final int PRIZE_AMOUNT = 100000; public static final String SERVICE_BANK_ACCOUNT = "123-123"; public static final int TICKET_PRIZE = 3; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index a9dff7fd0f41..6a032462dc83 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -39,7 +39,6 @@ public class LotteryService { private final LotteryTicketRepository repository; private final LotteryEventLog notifications; private final WireTransfers wireTransfers; - private final LotteryTicketChecker checker; /** * Constructor @@ -50,7 +49,6 @@ public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifi this.repository = repository; this.notifications = notifications; this.wireTransfers = wireTransfers; - this.checker = new LotteryTicketChecker(this.repository); } /** @@ -74,6 +72,6 @@ public Optional submitTicket(LotteryTicket ticket) { * Check if lottery ticket has won */ public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return checker.checkTicketForPrize(id, winningNumbers); + return new LotteryTicketChecker(repository).checkTicketForPrize(id, winningNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java index 3e9ebdae43c6..9fa318e4c8ef 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -36,19 +36,12 @@ public class LotteryTicket { /** * Constructor. */ - private LotteryTicket(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { + public LotteryTicket(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { this.id = id; playerDetails = details; lotteryNumbers = numbers; } - /** - * Factory for creating lottery tickets; - */ - public static LotteryTicket create(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { - return new LotteryTicket(id, details, numbers); - } - /** * @return player details */ diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java index 1061ad5535ce..7af115a7cee8 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -36,19 +36,12 @@ public class PlayerDetails { /** * Constructor. */ - private PlayerDetails(String email, String bankAccount, String phone) { + public PlayerDetails(String email, String bankAccount, String phone) { emailAddress = email; bankAccountNumber = bankAccount; phoneNumber = phone; } - - /** - * Factory for creating new objects. - */ - public static PlayerDetails create(String email, String bankAccount, String phone) { - return new PlayerDetails(email, bankAccount, phone); - } - + /** * @return email */ diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java index b9c779c340c8..13458d02ac52 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java @@ -43,45 +43,45 @@ public class SampleData { static { PLAYERS = new ArrayList<>(); - PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); - PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); - PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); - PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); - PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); - PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); - PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); - PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); - PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); - PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); - PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); - PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); - PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); - PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); - PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); - PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); - PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); - PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); - PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); - PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); - PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); - PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); - PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); - PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); - PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); - PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); - PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); - PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); - PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); - PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); - PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); - PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); - PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); - PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); - PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); - PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); - PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + PLAYERS.add(new PlayerDetails("john@google.com", "312-342", "+3242434242")); + PLAYERS.add(new PlayerDetails("mary@google.com", "234-987", "+23452346")); + PLAYERS.add(new PlayerDetails("steve@google.com", "833-836", "+63457543")); + PLAYERS.add(new PlayerDetails("wayne@google.com", "319-826", "+24626")); + PLAYERS.add(new PlayerDetails("johnie@google.com", "983-322", "+3635635")); + PLAYERS.add(new PlayerDetails("andy@google.com", "934-734", "+0898245")); + PLAYERS.add(new PlayerDetails("richard@google.com", "536-738", "+09845325")); + PLAYERS.add(new PlayerDetails("kevin@google.com", "453-936", "+2423532")); + PLAYERS.add(new PlayerDetails("arnold@google.com", "114-988", "+5646346524")); + PLAYERS.add(new PlayerDetails("ian@google.com", "663-765", "+928394235")); + PLAYERS.add(new PlayerDetails("robin@google.com", "334-763", "+35448")); + PLAYERS.add(new PlayerDetails("ted@google.com", "735-964", "+98752345")); + PLAYERS.add(new PlayerDetails("larry@google.com", "734-853", "+043842423")); + PLAYERS.add(new PlayerDetails("calvin@google.com", "334-746", "+73294135")); + PLAYERS.add(new PlayerDetails("jacob@google.com", "444-766", "+358042354")); + PLAYERS.add(new PlayerDetails("edwin@google.com", "895-345", "+9752435")); + PLAYERS.add(new PlayerDetails("mary@google.com", "760-009", "+34203542")); + PLAYERS.add(new PlayerDetails("lolita@google.com", "425-907", "+9872342")); + PLAYERS.add(new PlayerDetails("bruno@google.com", "023-638", "+673824122")); + PLAYERS.add(new PlayerDetails("peter@google.com", "335-886", "+5432503945")); + PLAYERS.add(new PlayerDetails("warren@google.com", "225-946", "+9872341324")); + PLAYERS.add(new PlayerDetails("monica@google.com", "265-748", "+134124")); + PLAYERS.add(new PlayerDetails("ollie@google.com", "190-045", "+34453452")); + PLAYERS.add(new PlayerDetails("yngwie@google.com", "241-465", "+9897641231")); + PLAYERS.add(new PlayerDetails("lars@google.com", "746-936", "+42345298345")); + PLAYERS.add(new PlayerDetails("bobbie@google.com", "946-384", "+79831742")); + PLAYERS.add(new PlayerDetails("tyron@google.com", "310-992", "+0498837412")); + PLAYERS.add(new PlayerDetails("tyrell@google.com", "032-045", "+67834134")); + PLAYERS.add(new PlayerDetails("nadja@google.com", "000-346", "+498723")); + PLAYERS.add(new PlayerDetails("wendy@google.com", "994-989", "+987324454")); + PLAYERS.add(new PlayerDetails("luke@google.com", "546-634", "+987642435")); + PLAYERS.add(new PlayerDetails("bjorn@google.com", "342-874", "+7834325")); + PLAYERS.add(new PlayerDetails("lisa@google.com", "024-653", "+980742154")); + PLAYERS.add(new PlayerDetails("anton@google.com", "834-935", "+876423145")); + PLAYERS.add(new PlayerDetails("bruce@google.com", "284-936", "+09843212345")); + PLAYERS.add(new PlayerDetails("ray@google.com", "843-073", "+678324123")); + PLAYERS.add(new PlayerDetails("ron@google.com", "637-738", "+09842354")); + PLAYERS.add(new PlayerDetails("xavier@google.com", "143-947", "+375245")); + PLAYERS.add(new PlayerDetails("harriet@google.com", "842-404", "+131243252")); InMemoryBank wireTransfers = new InMemoryBank(); Random random = new Random(); for (int i = 0; i < PLAYERS.size(); i++) { @@ -95,7 +95,7 @@ public class SampleData { */ public static void submitTickets(LotteryService lotteryService, int numTickets) { for (int i = 0; i < numTickets; i++) { - LotteryTicket ticket = LotteryTicket.create(new LotteryTicketId(), + LotteryTicket ticket = new LotteryTicket(new LotteryTicketId(), getRandomPlayerDetails(), LotteryNumbers.createRandom()); lotteryService.submitTicket(ticket); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index eb791ca552b7..cc13d389d248 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -76,7 +76,7 @@ public static void main(String[] args) { String account = readString(scanner); System.out.println("What is your phone number?"); String phone = readString(scanner); - PlayerDetails details = PlayerDetails.create(email, account, phone); + PlayerDetails details = new PlayerDetails(email, account, phone); System.out.println("Give 4 comma separated lottery numbers?"); String numbers = readString(scanner); try { @@ -86,7 +86,7 @@ public static void main(String[] args) { chosen.add(Integer.parseInt(parts[i])); } LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); - LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); + LotteryTicket lotteryTicket = new LotteryTicket(new LotteryTicketId(), details, lotteryNumbers); Optional id = service.submitTicket(lotteryTicket); if (id.isPresent()) { System.out.println("Submitted lottery ticket with id: " + id.get()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index bbd95b38ddf1..e30468f9922f 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -76,9 +76,9 @@ public void testNextId() { @Test public void testCrudOperations() { // create new lottery ticket and save it - PlayerDetails details = PlayerDetails.create("foo@bar.com", "123-123", "07001234"); + PlayerDetails details = new PlayerDetails("foo@bar.com", "123-123", "07001234"); LotteryNumbers random = LotteryNumbers.createRandom(); - LotteryTicket original = LotteryTicket.create(new LotteryTicketId(), details, random); + LotteryTicket original = new LotteryTicket(new LotteryTicketId(), details, random); Optional saved = repository.save(original); assertEquals(1, repository.getTicketsCollection().count()); assertTrue(saved.isPresent()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java index 4840dc897771..ce1e6b4b06ae 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -34,16 +34,16 @@ public class LotteryTicketTest { @Test public void testEquals() { - PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + PlayerDetails details1 = new PlayerDetails("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket1 = LotteryTicket.create(new LotteryTicketId(), details1, numbers1); - PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryTicket ticket1 = new LotteryTicket(new LotteryTicketId(), details1, numbers1); + PlayerDetails details2 = new PlayerDetails("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket2 = LotteryTicket.create(new LotteryTicketId(), details2, numbers2); + LotteryTicket ticket2 = new LotteryTicket(new LotteryTicketId(), details2, numbers2); assertEquals(ticket1, ticket2); - PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); + PlayerDetails details3 = new PlayerDetails("elsa@foo.bar", "1223-121212", "+49332322"); LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); - LotteryTicket ticket3 = LotteryTicket.create(new LotteryTicketId(), details3, numbers3); + LotteryTicket ticket3 = new LotteryTicket(new LotteryTicketId(), details3, numbers3); assertFalse(ticket1.equals(ticket3)); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java index 813b035a2629..53aa2d9d594f 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -36,10 +36,10 @@ public class PlayerDetailsTest { @Test public void testEquals() { - PlayerDetails details1 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); - PlayerDetails details2 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details1 = new PlayerDetails("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details2 = new PlayerDetails("tom@foo.bar", "11212-123434", "+12323425"); assertEquals(details1, details2); - PlayerDetails details3 = PlayerDetails.create("john@foo.bar", "16412-123439", "+34323432"); + PlayerDetails details3 = new PlayerDetails("john@foo.bar", "16412-123439", "+34323432"); assertFalse(details1.equals(details3)); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java index 9b9f14c78253..d02694826f4b 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java @@ -59,7 +59,7 @@ public void testSetup() { @Test public void testFundTransfers() { - PlayerDetails playerDetails = PlayerDetails.create("john@wayne.com", "000-000", "03432534543"); + PlayerDetails playerDetails = new PlayerDetails("john@wayne.com", "000-000", "03432534543"); mongoEventLog.prizeError(playerDetails, 1000); assertEquals(1, mongoEventLog.getEventsCollection().count()); mongoEventLog.prizeError(playerDetails, 1000); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java index 02304296fbe8..ae15cd3d7e43 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -50,8 +50,8 @@ public static LotteryTicket createLotteryTicket() { */ public static LotteryTicket createLotteryTicket(String email, String account, String phone, Set givenNumbers) { - PlayerDetails details = PlayerDetails.create(email, account, phone); + PlayerDetails details = new PlayerDetails(email, account, phone); LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); - return LotteryTicket.create(new LotteryTicketId(), details, numbers); + return new LotteryTicket(new LotteryTicketId(), details, numbers); } } From b030cd4ebafcdcb017cbec979ec58b0de663ceba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 17 Sep 2016 09:20:33 +0300 Subject: [PATCH 182/207] Hexagonal pattern: Introduce lottery utils class --- .../hexagonal/domain/LotteryAdministration.java | 2 +- .../iluwatar/hexagonal/domain/LotteryService.java | 2 +- ...LotteryTicketChecker.java => LotteryUtils.java} | 14 ++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/domain/{LotteryTicketChecker.java => LotteryUtils.java} (83%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index d544c84a1991..bc264a9ef7c6 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -65,7 +65,7 @@ public LotteryNumbers performLottery() { LotteryNumbers numbers = LotteryNumbers.createRandom(); Map tickets = getAllSubmittedTickets(); for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = new LotteryTicketChecker(repository).checkTicketForPrize(id, numbers); + LotteryTicketCheckResult result = LotteryUtils.checkTicketForPrize(repository, id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 6a032462dc83..dceac26e4e8e 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -72,6 +72,6 @@ public Optional submitTicket(LotteryTicket ticket) { * Check if lottery ticket has won */ public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return new LotteryTicketChecker(repository).checkTicketForPrize(id, winningNumbers); + return LotteryUtils.checkTicketForPrize(repository, id, winningNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryUtils.java similarity index 83% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryUtils.java index ce193386b9ad..dc34c7f7da82 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryUtils.java @@ -27,20 +27,18 @@ import java.util.Optional; /** - * Lottery ticket checker + * Lottery utilities */ -public class LotteryTicketChecker { +public class LotteryUtils { - private final LotteryTicketRepository repository; - - public LotteryTicketChecker(LotteryTicketRepository repository) { - this.repository = repository; + private LotteryUtils() { } /** - * Check if lottery ticket has won + * Checks if lottery ticket has won */ - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + public static LotteryTicketCheckResult checkTicketForPrize(LotteryTicketRepository repository, LotteryTicketId id, + LotteryNumbers winningNumbers) { Optional optional = repository.findById(id); if (optional.isPresent()) { if (optional.get().getNumbers().equals(winningNumbers)) { From 6026eedd51b9753250051b620312bbedd2ccd6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 18 Sep 2016 17:51:09 +0300 Subject: [PATCH 183/207] UML generation: Mark the urm-maven-plugin execution to be ignored in Eclipse and recreate all .puml files --- .../etc/abstract-document.urm.puml | 36 +-- .../etc/abstract-factory.urm.puml | 72 ++--- adapter/etc/adapter.urm.puml | 22 +- .../etc/aggregator-service.urm.puml | 38 +-- .../etc/inventory-microservice.urm.puml | 8 +- api-gateway/etc/api-gateway-service.urm.puml | 28 +- .../etc/async-method-invocation.urm.puml | 26 +- bridge/etc/bridge.urm.puml | 62 ++--- builder/etc/builder.urm.puml | 86 +++--- .../etc/business-delegate.urm.puml | 22 +- caching/etc/caching.urm.puml | 50 ++-- callback/etc/callback.urm.puml | 24 +- chain/etc/chain.urm.puml | 40 +-- command/etc/command.urm.puml | 62 ++--- composite/etc/composite.urm.puml | 30 +- dao/etc/dao.urm.puml | 70 ++--- decorator/etc/decorator.urm.puml | 10 +- delegation/etc/delegation.urm.puml | 20 +- .../etc/dependency-injection.urm.puml | 44 +-- double-dispatch/etc/double-dispatch.urm.puml | 28 +- .../etc/event-aggregator.urm.puml | 54 ++-- .../etc/event-driven-architecture.urm.puml | 28 +- execute-around/etc/execute-around.urm.puml | 6 +- facade/etc/facade.urm.puml | 18 +- factory-kit/etc/factory-kit.urm.puml | 36 +-- factory-method/etc/factory-method.urm.puml | 6 +- feature-toggle/etc/feature-toggle.urm.puml | 16 +- fluentinterface/etc/fluentinterface.urm.puml | 16 +- flux/etc/flux.urm.puml | 80 +++--- flyweight/etc/flyweight.urm.puml | 46 +-- .../etc/front-controller.urm.puml | 46 +-- .../etc/half-sync-half-async.urm.puml | 26 +- hexagonal/etc/hexagonal.urm.puml | 263 ++++++++++++------ .../etc/intercepting-filter.urm.puml | 70 ++--- interpreter/etc/interpreter.urm.puml | 32 +-- iterator/etc/iterator.urm.puml | 22 +- layers/etc/layers.urm.puml | 118 ++++---- lazy-loading/etc/lazy-loading.urm.puml | 20 +- mediator/etc/mediator.urm.puml | 38 +-- memento/etc/memento.urm.puml | 24 +- .../etc/model-view-controller.urm.puml | 34 +-- .../etc/model-view-presenter.urm.puml | 78 +++--- monostate/etc/monostate.urm.puml | 16 +- mute-idiom/etc/mute-idiom.urm.puml | 6 +- mutex/etc/mutex.urm.puml | 12 +- naked-objects/etc/naked-objects-dom.urm.puml | 47 +--- .../etc/naked-objects-fixture.urm.puml | 10 +- .../etc/naked-objects-integtests.urm.puml | 26 +- null-object/etc/null-object.urm.puml | 28 +- object-pool/etc/object-pool.urm.puml | 16 +- observer/etc/observer.urm.puml | 36 +-- poison-pill/etc/poison-pill.urm.puml | 46 +-- pom.xml | 19 ++ .../etc/private-class-data.urm.puml | 20 +- .../etc/producer-consumer.urm.puml | 26 +- promise/etc/promise.urm.puml | 36 +-- property/etc/property.urm.puml | 32 +-- prototype/etc/prototype.urm.puml | 74 ++--- proxy/etc/proxy.urm.puml | 12 +- reactor/etc/reactor.urm.puml | 138 ++++----- .../etc/reader-writer-lock.urm.puml | 36 +-- repository/etc/repository.urm.puml | 50 ++-- ...rce-acquisition-is-initialization.urm.puml | 8 +- semaphore/etc/semaphore.urm.puml | 42 +-- servant/etc/servant.urm.puml | 40 +-- service-layer/etc/service-layer.urm.puml | 76 ++--- service-locator/etc/service-locator.urm.puml | 30 +- singleton/etc/singleton.urm.puml | 22 +- specification/etc/specification.urm.puml | 64 ++--- state/etc/state.urm.puml | 32 +-- step-builder/etc/step-builder.urm.puml | 36 +-- strategy/etc/strategy.urm.puml | 26 +- template-method/etc/template-method.urm.puml | 26 +- thread-pool/etc/thread-pool.urm.puml | 28 +- tolerant-reader/etc/tolerant-reader.urm.puml | 18 +- twin/etc/twin.urm.puml | 18 +- value-object/etc/value-object.urm.puml | 8 +- visitor/etc/visitor.urm.puml | 54 ++-- 78 files changed, 1568 insertions(+), 1501 deletions(-) diff --git a/abstract-document/etc/abstract-document.urm.puml b/abstract-document/etc/abstract-document.urm.puml index c738b50ce786..fa15eb3c759d 100644 --- a/abstract-document/etc/abstract-document.urm.puml +++ b/abstract-document/etc/abstract-document.urm.puml @@ -3,31 +3,30 @@ package com.iluwatar.abstractdocument.domain { class Part { + Part(properties : Map) } - class Car { - + Car(properties : Map) - } - interface HasModel { + interface HasPrice { + PROPERTY : String {static} - + getModel() : Optional + + getPrice() : Optional } interface HasParts { + PROPERTY : String {static} + getParts() : Stream } + class Car { + + Car(properties : Map) + } interface HasType { + PROPERTY : String {static} + getType() : Optional } - interface HasPrice { + interface HasModel { + PROPERTY : String {static} - + getPrice() : Optional + + getModel() : Optional } } package com.iluwatar.abstractdocument { - interface Document { - + children(String, Function, T>) : Stream {abstract} - + get(String) : Object {abstract} - + put(String, Object) {abstract} + class App { + + App() + + main(args : String[]) {static} } abstract class AbstractDocument { - properties : Map @@ -37,9 +36,10 @@ package com.iluwatar.abstractdocument { + put(key : String, value : Object) + toString() : String } - class App { - + App() - + main(args : String[]) {static} + interface Document { + + children(String, Function, T>) : Stream {abstract} + + get(String) : Object {abstract} + + put(String, Object) {abstract} } } AbstractDocument --+ Map @@ -47,13 +47,13 @@ Part ..|> HasType Part ..|> HasModel Part ..|> HasPrice Part --|> AbstractDocument +AbstractDocument ..|> Document +HasPrice --|> Document +HasParts --|> Document Car ..|> HasModel Car ..|> HasPrice Car ..|> HasParts Car --|> AbstractDocument -HasModel --|> Document -HasParts --|> Document -AbstractDocument ..|> Document HasType --|> Document -HasPrice --|> Document +HasModel --|> Document @enduml \ No newline at end of file diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml index 88402c6d76a9..9648a6a96049 100644 --- a/abstract-factory/etc/abstract-factory.urm.puml +++ b/abstract-factory/etc/abstract-factory.urm.puml @@ -1,22 +1,5 @@ @startuml package com.iluwatar.abstractfactory { - interface Castle { - + getDescription() : String {abstract} - } - class OrcKingdomFactory { - + OrcKingdomFactory() - + createArmy() : Army - + createCastle() : Castle - + createKing() : King - } - class ElfKing { - ~ DESCRIPTION : String {static} - + ElfKing() - + getDescription() : String - } - interface King { - + getDescription() : String {abstract} - } class App { - army : Army - castle : Castle @@ -34,23 +17,20 @@ package com.iluwatar.abstractfactory { - setCastle(castle : Castle) - setKing(king : King) } - class OrcKing { - ~ DESCRIPTION : String {static} - + OrcKing() - + getDescription() : String - } - class ElfKingdomFactory { - + ElfKingdomFactory() + class OrcKingdomFactory { + + OrcKingdomFactory() + createArmy() : Army + createCastle() : Castle + createKing() : King } - interface Army { - + getDescription() : String {abstract} + class ElfCastle { + ~ DESCRIPTION : String {static} + + ElfCastle() + + getDescription() : String } - class OrcArmy { + class OrcCastle { ~ DESCRIPTION : String {static} - + OrcArmy() + + OrcCastle() + getDescription() : String } interface KingdomFactory { @@ -58,31 +38,51 @@ package com.iluwatar.abstractfactory { + createCastle() : Castle {abstract} + createKing() : King {abstract} } + class ElfKing { + ~ DESCRIPTION : String {static} + + ElfKing() + + getDescription() : String + } class ElfArmy { ~ DESCRIPTION : String {static} + ElfArmy() + getDescription() : String } - class ElfCastle { + interface Castle { + + getDescription() : String {abstract} + } + interface Army { + + getDescription() : String {abstract} + } + class OrcKing { ~ DESCRIPTION : String {static} - + ElfCastle() + + OrcKing() + getDescription() : String } - class OrcCastle { + class OrcArmy { ~ DESCRIPTION : String {static} - + OrcCastle() + + OrcArmy() + getDescription() : String } + interface King { + + getDescription() : String {abstract} + } + class ElfKingdomFactory { + + ElfKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } } App --> "-castle" Castle App --> "-king" King App --> "-army" Army OrcKingdomFactory ..|> KingdomFactory +ElfCastle ..|> Castle +OrcCastle ..|> Castle ElfKing ..|> King +ElfArmy ..|> Army OrcKing ..|> King -ElfKingdomFactory ..|> KingdomFactory OrcArmy ..|> Army -ElfArmy ..|> Army -ElfCastle ..|> Castle -OrcCastle ..|> Castle +ElfKingdomFactory ..|> KingdomFactory @enduml \ No newline at end of file diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml index 2cee13dc434a..a7c962a3b09b 100644 --- a/adapter/etc/adapter.urm.puml +++ b/adapter/etc/adapter.urm.puml @@ -1,13 +1,19 @@ @startuml package com.iluwatar.adapter { - class App { - + App() - + main(args : String[]) {static} - } interface BattleShip { + fire() {abstract} + move() {abstract} } + class BattleFishingBoat { + - boat : FishingBoat + + BattleFishingBoat() + + fire() + + move() + } + class App { + + App() + + main(args : String[]) {static} + } class Captain { - battleship : BattleShip + Captain() @@ -16,12 +22,6 @@ package com.iluwatar.adapter { + move() + setBattleship(battleship : BattleShip) } - class BattleFishingBoat { - - boat : FishingBoat - + BattleFishingBoat() - + fire() - + move() - } class FishingBoat { + FishingBoat() + fish() @@ -30,6 +30,6 @@ package com.iluwatar.adapter { } BattleFishingBoat --> "-boat" FishingBoat Captain --> "-battleship" BattleShip -Captain ..|> BattleShip BattleFishingBoat ..|> BattleShip +Captain ..|> BattleShip @enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-service.urm.puml b/aggregator-microservices/etc/aggregator-service.urm.puml index 5c2e1167ab9c..2a6600531e55 100644 --- a/aggregator-microservices/etc/aggregator-service.urm.puml +++ b/aggregator-microservices/etc/aggregator-service.urm.puml @@ -1,17 +1,15 @@ @startuml package com.iluwatar.aggregator.microservices { - class Aggregator { - - informationClient : ProductInformationClient - - inventoryClient : ProductInventoryClient - + Aggregator() - + getProduct() : Product + class ProductInventoryClientImpl { + + ProductInventoryClientImpl() + + getProductInventories() : int } - class ProductInformationClientImpl { - + ProductInformationClientImpl() - + getProductTitle() : String + class App { + + App() + + main(args : String[]) {static} } - interface ProductInformationClient { - + getProductTitle() : String {abstract} + interface ProductInventoryClient { + + getProductInventories() : int {abstract} } class Product { - productInventories : int @@ -22,20 +20,22 @@ package com.iluwatar.aggregator.microservices { + setProductInventories(productInventories : int) + setTitle(title : String) } - class ProductInventoryClientImpl { - + ProductInventoryClientImpl() - + getProductInventories() : int + class Aggregator { + - informationClient : ProductInformationClient + - inventoryClient : ProductInventoryClient + + Aggregator() + + getProduct() : Product } - class App { - + App() - + main(args : String[]) {static} + class ProductInformationClientImpl { + + ProductInformationClientImpl() + + getProductTitle() : String } - interface ProductInventoryClient { - + getProductInventories() : int {abstract} + interface ProductInformationClient { + + getProductTitle() : String {abstract} } } Aggregator --> "-inventoryClient" ProductInventoryClient Aggregator --> "-informationClient" ProductInformationClient -ProductInformationClientImpl ..|> ProductInformationClient ProductInventoryClientImpl ..|> ProductInventoryClient +ProductInformationClientImpl ..|> ProductInformationClient @enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/inventory-microservice.urm.puml b/aggregator-microservices/etc/inventory-microservice.urm.puml index 90f327e07db8..a07a36306376 100644 --- a/aggregator-microservices/etc/inventory-microservice.urm.puml +++ b/aggregator-microservices/etc/inventory-microservice.urm.puml @@ -1,12 +1,12 @@ @startuml package com.iluwatar.inventory.microservice { - class InventoryApplication { - + InventoryApplication() - + main(args : String[]) {static} - } class InventoryController { + InventoryController() + getProductInventories() : int } + class InventoryApplication { + + InventoryApplication() + + main(args : String[]) {static} + } } @enduml \ No newline at end of file diff --git a/api-gateway/etc/api-gateway-service.urm.puml b/api-gateway/etc/api-gateway-service.urm.puml index 3313f7059cc5..409e7ee91842 100644 --- a/api-gateway/etc/api-gateway-service.urm.puml +++ b/api-gateway/etc/api-gateway-service.urm.puml @@ -1,7 +1,16 @@ @startuml package com.iluwatar.api.gateway { - interface ImageClient { - + getImagePath() : String {abstract} + class App { + + App() + + main(args : String[]) {static} + } + class PriceClientImpl { + + PriceClientImpl() + + getPrice() : String + } + class ImageClientImpl { + + ImageClientImpl() + + getImagePath() : String } class MobileProduct { - price : String @@ -9,6 +18,9 @@ package com.iluwatar.api.gateway { + getPrice() : String + setPrice(price : String) } + interface ImageClient { + + getImagePath() : String {abstract} + } class ApiGateway { - imageClient : ImageClient - priceClient : PriceClient @@ -28,18 +40,6 @@ package com.iluwatar.api.gateway { interface PriceClient { + getPrice() : String {abstract} } - class PriceClientImpl { - + PriceClientImpl() - + getPrice() : String - } - class ImageClientImpl { - + ImageClientImpl() - + getImagePath() : String - } - class App { - + App() - + main(args : String[]) {static} - } } ApiGateway --> "-imageClient" ImageClient ApiGateway --> "-priceClient" PriceClient diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml index 9a90d307ecfd..b96e843d3f7a 100644 --- a/async-method-invocation/etc/async-method-invocation.urm.puml +++ b/async-method-invocation/etc/async-method-invocation.urm.puml @@ -1,13 +1,22 @@ @startuml package com.iluwatar.async.method.invocation { - interface AsyncCallback { - + onComplete(T, Optional) {abstract} + class App { + + App() + - callback(name : String) : AsyncCallback {static} + - lazyval(value : T, delayMillis : long) : Callable {static} + - log(msg : String) {static} + + main(args : String[]) {static} } interface AsyncResult { + await() {abstract} + getValue() : T {abstract} + isCompleted() : boolean {abstract} } + interface AsyncExecutor { + + endProcess(AsyncResult) : T {abstract} + + startProcess(Callable) : AsyncResult {abstract} + + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} + } class ThreadAsyncExecutor { - idx : AtomicInteger + ThreadAsyncExecutor() @@ -15,12 +24,8 @@ package com.iluwatar.async.method.invocation { + startProcess(task : Callable) : AsyncResult + startProcess(task : Callable, callback : AsyncCallback) : AsyncResult } - class App { - + App() - - callback(name : String) : AsyncCallback {static} - - lazyval(value : T, delayMillis : long) : Callable {static} - - log(msg : String) {static} - + main(args : String[]) {static} + interface AsyncCallback { + + onComplete(T, Optional) {abstract} } -class CompletableResult { ~ COMPLETED : int {static} @@ -38,11 +43,6 @@ package com.iluwatar.async.method.invocation { ~ setException(exception : Exception) ~ setValue(value : T) } - interface AsyncExecutor { - + endProcess(AsyncResult) : T {abstract} - + startProcess(Callable) : AsyncResult {abstract} - + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} - } } CompletableResult ..+ ThreadAsyncExecutor ThreadAsyncExecutor ..|> AsyncExecutor diff --git a/bridge/etc/bridge.urm.puml b/bridge/etc/bridge.urm.puml index d9d7a41456f0..84e250a06e50 100644 --- a/bridge/etc/bridge.urm.puml +++ b/bridge/etc/bridge.urm.puml @@ -8,26 +8,6 @@ package com.iluwatar.bridge { + unwield() + wield() } - abstract class MagicWeapon { - # imp : MagicWeaponImpl - + MagicWeapon(imp : MagicWeaponImpl) - + getImp() : MagicWeaponImpl - + swing() {abstract} - + unwield() {abstract} - + wield() {abstract} - } - abstract class SoulEatingMagicWeaponImpl { - + SoulEatingMagicWeaponImpl() - + eatSoulImp() {abstract} - } - class BlindingMagicWeapon { - + BlindingMagicWeapon(imp : BlindingMagicWeaponImpl) - + blind() - + getImp() : BlindingMagicWeaponImpl - + swing() - + unwield() - + wield() - } class Stormbringer { + Stormbringer() + eatSoulImp() @@ -35,9 +15,9 @@ package com.iluwatar.bridge { + unwieldImp() + wieldImp() } - abstract class BlindingMagicWeaponImpl { - + BlindingMagicWeaponImpl() - + blindImp() {abstract} + abstract class FlyingMagicWeaponImpl { + + FlyingMagicWeaponImpl() + + flyImp() {abstract} } class SoulEatingMagicWeapon { + SoulEatingMagicWeapon(imp : SoulEatingMagicWeaponImpl) @@ -53,6 +33,10 @@ package com.iluwatar.bridge { + unwieldImp() {abstract} + wieldImp() {abstract} } + abstract class SoulEatingMagicWeaponImpl { + + SoulEatingMagicWeaponImpl() + + eatSoulImp() {abstract} + } class Excalibur { + Excalibur() + blindImp() @@ -60,10 +44,6 @@ package com.iluwatar.bridge { + unwieldImp() + wieldImp() } - abstract class FlyingMagicWeaponImpl { - + FlyingMagicWeaponImpl() - + flyImp() {abstract} - } class Mjollnir { + Mjollnir() + flyImp() @@ -75,15 +55,35 @@ package com.iluwatar.bridge { + App() + main(args : String[]) {static} } + abstract class MagicWeapon { + # imp : MagicWeaponImpl + + MagicWeapon(imp : MagicWeaponImpl) + + getImp() : MagicWeaponImpl + + swing() {abstract} + + unwield() {abstract} + + wield() {abstract} + } + abstract class BlindingMagicWeaponImpl { + + BlindingMagicWeaponImpl() + + blindImp() {abstract} + } + class BlindingMagicWeapon { + + BlindingMagicWeapon(imp : BlindingMagicWeaponImpl) + + blind() + + getImp() : BlindingMagicWeaponImpl + + swing() + + unwield() + + wield() + } } MagicWeapon --> "-imp" MagicWeaponImpl FlyingMagicWeapon --|> MagicWeapon -SoulEatingMagicWeaponImpl --|> MagicWeaponImpl -BlindingMagicWeapon --|> MagicWeapon Stormbringer --|> SoulEatingMagicWeaponImpl -BlindingMagicWeaponImpl --|> MagicWeaponImpl +FlyingMagicWeaponImpl --|> MagicWeaponImpl SoulEatingMagicWeapon --|> MagicWeapon +SoulEatingMagicWeaponImpl --|> MagicWeaponImpl Excalibur --|> BlindingMagicWeaponImpl -FlyingMagicWeaponImpl --|> MagicWeaponImpl Mjollnir --|> FlyingMagicWeaponImpl +BlindingMagicWeaponImpl --|> MagicWeaponImpl +BlindingMagicWeapon --|> MagicWeapon @enduml \ No newline at end of file diff --git a/builder/etc/builder.urm.puml b/builder/etc/builder.urm.puml index 2624763297ea..0c98f3b4b753 100644 --- a/builder/etc/builder.urm.puml +++ b/builder/etc/builder.urm.puml @@ -1,48 +1,58 @@ @startuml package com.iluwatar.builder { - class Hero { + class Builder { - armor : Armor - hairColor : HairColor - hairType : HairType - name : String - profession : Profession - weapon : Weapon - - Hero(builder : Builder) - + getArmor() : Armor - + getHairColor() : HairColor - + getHairType() : HairType - + getName() : String - + getProfession() : Profession - + getWeapon() : Weapon - + toString() : String + + Builder(profession : Profession, name : String) + + build() : Hero + + withArmor(armor : Armor) : Builder + + withHairColor(hairColor : HairColor) : Builder + + withHairType(hairType : HairType) : Builder + + withWeapon(weapon : Weapon) : Builder } class App { + App() + main(args : String[]) {static} } - class Builder { + class Hero { - armor : Armor - hairColor : HairColor - hairType : HairType - name : String - profession : Profession - weapon : Weapon - + Builder(profession : Profession, name : String) - + build() : Hero - + withArmor(armor : Armor) : Builder - + withHairColor(hairColor : HairColor) : Builder - + withHairType(hairType : HairType) : Builder - + withWeapon(weapon : Weapon) : Builder + - Hero(builder : Builder) + + getArmor() : Armor + + getHairColor() : HairColor + + getHairType() : HairType + + getName() : String + + getProfession() : Profession + + getWeapon() : Weapon + + toString() : String } - enum Armor { - + CHAIN_MAIL {static} - + CLOTHES {static} - + LEATHER {static} - + PLATE_MAIL {static} - - title : String + enum Weapon { + + AXE {static} + + BOW {static} + + DAGGER {static} + + SWORD {static} + + WARHAMMER {static} + toString() : String - + valueOf(name : String) : Armor {static} - + values() : Armor[] {static} + + valueOf(name : String) : Weapon {static} + + values() : Weapon[] {static} + } + enum HairColor { + + BLACK {static} + + BLOND {static} + + BROWN {static} + + RED {static} + + WHITE {static} + + toString() : String + + valueOf(name : String) : HairColor {static} + + values() : HairColor[] {static} } enum Profession { + MAGE {static} @@ -53,15 +63,15 @@ package com.iluwatar.builder { + valueOf(name : String) : Profession {static} + values() : Profession[] {static} } - enum Weapon { - + AXE {static} - + BOW {static} - + DAGGER {static} - + SWORD {static} - + WARHAMMER {static} + enum Armor { + + CHAIN_MAIL {static} + + CLOTHES {static} + + LEATHER {static} + + PLATE_MAIL {static} + - title : String + toString() : String - + valueOf(name : String) : Weapon {static} - + values() : Weapon[] {static} + + valueOf(name : String) : Armor {static} + + values() : Armor[] {static} } enum HairType { + BALD {static} @@ -74,19 +84,9 @@ package com.iluwatar.builder { + valueOf(name : String) : HairType {static} + values() : HairType[] {static} } - enum HairColor { - + BLACK {static} - + BLOND {static} - + BROWN {static} - + RED {static} - + WHITE {static} - + toString() : String - + valueOf(name : String) : HairColor {static} - + values() : HairColor[] {static} - } } -Builder ..+ Hero Hero --> "-profession" Profession +Builder ..+ Hero Hero --> "-armor" Armor App --+ Hero Builder --> "-weapon" Weapon diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml index a8f31700b924..a58136c8ce8d 100644 --- a/business-delegate/etc/business-delegate.urm.puml +++ b/business-delegate/etc/business-delegate.urm.puml @@ -1,5 +1,9 @@ @startuml package com.iluwatar.business.delegate { + class EjbService { + + EjbService() + + doProcessing() + } class BusinessLookup { - ejbService : EjbService - jmsService : JmsService @@ -8,15 +12,18 @@ package com.iluwatar.business.delegate { + setEjbService(ejbService : EjbService) + setJmsService(jmsService : JmsService) } + class App { + + App() + + main(args : String[]) {static} + } + interface BusinessService { + + doProcessing() {abstract} + } class Client { - businessDelegate : BusinessDelegate + Client(businessDelegate : BusinessDelegate) + doTask() } - class EjbService { - + EjbService() - + doProcessing() - } class BusinessDelegate { - businessService : BusinessService - lookupService : BusinessLookup @@ -26,17 +33,10 @@ package com.iluwatar.business.delegate { + setLookupService(businessLookup : BusinessLookup) + setServiceType(serviceType : ServiceType) } - interface BusinessService { - + doProcessing() {abstract} - } class JmsService { + JmsService() + doProcessing() } - class App { - + App() - + main(args : String[]) {static} - } enum ServiceType { + EJB {static} + JMS {static} diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml index 273c9911c213..b8f4fb49b6b4 100644 --- a/caching/etc/caching.urm.puml +++ b/caching/etc/caching.urm.puml @@ -1,17 +1,18 @@ @startuml package com.iluwatar.caching { - class UserAccount { - - additionalInfo : String - - userId : String - - userName : String - + UserAccount(userId : String, userName : String, additionalInfo : String) - + getAdditionalInfo() : String - + getUserId() : String - + getUserName() : String - + setAdditionalInfo(additionalInfo : String) - + setUserId(userId : String) - + setUserName(userName : String) - + toString() : String + class App { + + App() + + main(args : String[]) {static} + + useReadAndWriteThroughStrategy() + + useReadThroughAndWriteAroundStrategy() + + useReadThroughAndWriteBehindStrategy() + } + ~class Node { + ~ next : Node + ~ previous : Node + ~ userAccount : UserAccount + ~ userId : String + + Node(this$0 : String, userId : UserAccount) } class CacheStore { ~ cache : LruCache {static} @@ -36,12 +37,18 @@ package com.iluwatar.caching { + printCacheContent() : String {static} + save(userAccount : UserAccount) {static} } - ~class Node { - ~ next : Node - ~ previous : Node - ~ userAccount : UserAccount - ~ userId : String - + Node(this$0 : LruCache, userId : String, userAccount : UserAccount) + class UserAccount { + - additionalInfo : String + - userId : String + - userName : String + + UserAccount(userId : String, userName : String, additionalInfo : String) + + getAdditionalInfo() : String + + getUserId() : String + + getUserName() : String + + setAdditionalInfo(additionalInfo : String) + + setUserId(userId : String) + + setUserName(userName : String) + + toString() : String } class LruCache { ~ cache : Map @@ -74,13 +81,6 @@ package com.iluwatar.caching { + upsertDb(userAccount : UserAccount) {static} + writeToDb(userAccount : UserAccount) {static} } - class App { - + App() - + main(args : String[]) {static} - + useReadAndWriteThroughStrategy() - + useReadThroughAndWriteAroundStrategy() - + useReadThroughAndWriteBehindStrategy() - } enum CachingPolicy { + AROUND {static} + BEHIND {static} diff --git a/callback/etc/callback.urm.puml b/callback/etc/callback.urm.puml index 8b27ee8a8906..b9e1ca694e44 100644 --- a/callback/etc/callback.urm.puml +++ b/callback/etc/callback.urm.puml @@ -1,24 +1,24 @@ @startuml package com.iluwatar.callback { - class LambdasApp { - + LambdasApp() - + main(args : String[]) {static} + interface Callback { + + call() {abstract} } - class SimpleTask { - + SimpleTask() - + execute() + abstract class Task { + + Task() + + execute() {abstract} + + executeWith(callback : Callback) } class App { + App() + main(args : String[]) {static} } - abstract class Task { - + Task() - + execute() {abstract} - + executeWith(callback : Callback) + class SimpleTask { + + SimpleTask() + + execute() } - interface Callback { - + call() {abstract} + class LambdasApp { + + LambdasApp() + + main(args : String[]) {static} } } SimpleTask --|> Task diff --git a/chain/etc/chain.urm.puml b/chain/etc/chain.urm.puml index c75cbc8d1841..21365765d0e3 100644 --- a/chain/etc/chain.urm.puml +++ b/chain/etc/chain.urm.puml @@ -1,13 +1,20 @@ @startuml package com.iluwatar.chain { + class OrcSoldier { + + OrcSoldier(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } class OrcCommander { + OrcCommander(handler : RequestHandler) + handleRequest(req : Request) + toString() : String } - class App { - + App() - + main(args : String[]) {static} + class OrcKing { + ~ chain : RequestHandler + + OrcKing() + - buildChain() + + makeRequest(req : Request) } class Request { - handled : boolean @@ -20,21 +27,9 @@ package com.iluwatar.chain { + markHandled() + toString() : String } - class OrcOfficer { - + OrcOfficer(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String - } - class OrcKing { - ~ chain : RequestHandler - + OrcKing() - - buildChain() - + makeRequest(req : Request) - } - class OrcSoldier { - + OrcSoldier(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String + class App { + + App() + + main(args : String[]) {static} } abstract class RequestHandler { - next : RequestHandler @@ -43,6 +38,11 @@ package com.iluwatar.chain { # printHandling(req : Request) + toString() : String {abstract} } + class OrcOfficer { + + OrcOfficer(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } enum RequestType { + COLLECT_TAX {static} + DEFEND_CASTLE {static} @@ -52,9 +52,9 @@ package com.iluwatar.chain { } } RequestHandler --> "-next" RequestHandler -OrcKing --> "-chain" RequestHandler Request --> "-requestType" RequestType +OrcKing --> "-chain" RequestHandler +OrcSoldier --|> RequestHandler OrcCommander --|> RequestHandler OrcOfficer --|> RequestHandler -OrcSoldier --|> RequestHandler @enduml \ No newline at end of file diff --git a/command/etc/command.urm.puml b/command/etc/command.urm.puml index 015fb30be54b..27bff7330605 100644 --- a/command/etc/command.urm.puml +++ b/command/etc/command.urm.puml @@ -1,5 +1,28 @@ @startuml package com.iluwatar.command { + class InvisibilitySpell { + - target : Target + + InvisibilitySpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Command { + + Command() + + execute(Target) {abstract} + + redo() {abstract} + + toString() : String {abstract} + + undo() {abstract} + } + class Goblin { + + Goblin() + + toString() : String + } abstract class Target { - size : Size - visibility : Visibility @@ -11,9 +34,14 @@ package com.iluwatar.command { + setVisibility(visibility : Visibility) + toString() : String {abstract} } - class Goblin { - + Goblin() + class Wizard { + - redoStack : Deque + - undoStack : Deque + + Wizard() + + castSpell(command : Command, target : Target) + + redoLastSpell() + toString() : String + + undoLastSpell() } class ShrinkSpell { - oldSize : Size @@ -24,34 +52,6 @@ package com.iluwatar.command { + toString() : String + undo() } - class InvisibilitySpell { - - target : Target - + InvisibilitySpell() - + execute(target : Target) - + redo() - + toString() : String - + undo() - } - class Wizard { - - redoStack : Deque - - undoStack : Deque - + Wizard() - + castSpell(command : Command, target : Target) - + redoLastSpell() - + toString() : String - + undoLastSpell() - } - class App { - + App() - + main(args : String[]) {static} - } - abstract class Command { - + Command() - + execute(Target) {abstract} - + redo() {abstract} - + toString() : String {abstract} - + undo() {abstract} - } enum Size { + LARGE {static} + NORMAL {static} @@ -78,7 +78,7 @@ ShrinkSpell --> "-oldSize" Size InvisibilitySpell --> "-target" Target ShrinkSpell --> "-target" Target Target --> "-visibility" Visibility +InvisibilitySpell --|> Command Goblin --|> Target ShrinkSpell --|> Command -InvisibilitySpell --|> Command @enduml \ No newline at end of file diff --git a/composite/etc/composite.urm.puml b/composite/etc/composite.urm.puml index 6f6e93c98673..82e9b65eb059 100644 --- a/composite/etc/composite.urm.puml +++ b/composite/etc/composite.urm.puml @@ -1,29 +1,29 @@ @startuml package com.iluwatar.composite { - class Letter { - - c : char - + Letter(c : char) - # printThisAfter() - # printThisBefore() - } - class Sentence { - + Sentence(words : List) - # printThisAfter() - # printThisBefore() - } class Word { + Word(letters : List) # printThisAfter() # printThisBefore() } + class App { + + App() + + main(args : String[]) {static} + } class Messenger { + Messenger() ~ messageFromElves() : LetterComposite ~ messageFromOrcs() : LetterComposite } - class App { - + App() - + main(args : String[]) {static} + class Letter { + - c : char + + Letter(c : char) + # printThisAfter() + # printThisBefore() + } + class Sentence { + + Sentence(words : List) + # printThisAfter() + # printThisBefore() } abstract class LetterComposite { - children : List @@ -36,7 +36,7 @@ package com.iluwatar.composite { } } LetterComposite --> "-children" LetterComposite +Word --|> LetterComposite Letter --|> LetterComposite Sentence --|> LetterComposite -Word --|> LetterComposite @enduml \ No newline at end of file diff --git a/dao/etc/dao.urm.puml b/dao/etc/dao.urm.puml index f751b967c45b..90a9c9725f78 100644 --- a/dao/etc/dao.urm.puml +++ b/dao/etc/dao.urm.puml @@ -1,39 +1,5 @@ @startuml package com.iluwatar.dao { - class Customer { - - firstName : String - - id : int - - lastName : String - + Customer(id : int, firstName : String, lastName : String) - + equals(that : Object) : boolean - + getFirstName() : String - + getId() : int - + getLastName() : String - + hashCode() : int - + setFirstName(firstName : String) - + setId(id : int) - + setLastName(lastName : String) - + toString() : String - } - interface CustomerDao { - + add(Customer) : boolean {abstract} - + delete(Customer) : boolean {abstract} - + getAll() : Stream {abstract} - + getById(int) : Optional {abstract} - + update(Customer) : boolean {abstract} - } - class DbCustomerDao { - - dataSource : DataSource - + DbCustomerDao(dataSource : DataSource) - + add(customer : Customer) : boolean - - createCustomer(resultSet : ResultSet) : Customer - + delete(customer : Customer) : boolean - + getAll() : Stream - + getById(id : int) : Optional - - getConnection() : Connection - - mutedClose(connection : Connection) - + update(customer : Customer) : boolean - } class InMemoryCustomerDao { - idToCustomer : Map + InMemoryCustomerDao() @@ -47,6 +13,13 @@ package com.iluwatar.dao { + CREATE_SCHEMA_SQL : String {static} + DELETE_SCHEMA_SQL : String {static} } + interface CustomerDao { + + add(Customer) : boolean {abstract} + + delete(Customer) : boolean {abstract} + + getAll() : Stream {abstract} + + getById(int) : Optional {abstract} + + update(Customer) : boolean {abstract} + } class App { - DB_URL : String {static} - log : Logger {static} @@ -59,7 +32,34 @@ package com.iluwatar.dao { + main(args : String[]) {static} - performOperationsUsing(customerDao : CustomerDao) {static} } + class DbCustomerDao { + - dataSource : DataSource + + DbCustomerDao(dataSource : DataSource) + + add(customer : Customer) : boolean + - createCustomer(resultSet : ResultSet) : Customer + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + - getConnection() : Connection + - mutedClose(connection : Connection) + + update(customer : Customer) : boolean + } + class Customer { + - firstName : String + - id : int + - lastName : String + + Customer(id : int, firstName : String, lastName : String) + + equals(that : Object) : boolean + + getFirstName() : String + + getId() : int + + getLastName() : String + + hashCode() : int + + setFirstName(firstName : String) + + setId(id : int) + + setLastName(lastName : String) + + toString() : String + } } -DbCustomerDao ..|> CustomerDao InMemoryCustomerDao ..|> CustomerDao +DbCustomerDao ..|> CustomerDao @enduml \ No newline at end of file diff --git a/decorator/etc/decorator.urm.puml b/decorator/etc/decorator.urm.puml index 6c44e6cc9d48..4862ddfeb47f 100644 --- a/decorator/etc/decorator.urm.puml +++ b/decorator/etc/decorator.urm.puml @@ -1,5 +1,10 @@ @startuml package com.iluwatar.decorator { + interface Hostile { + + attack() {abstract} + + fleeBattle() {abstract} + + getAttackPower() : int {abstract} + } class App { + App() + main(args : String[]) {static} @@ -10,11 +15,6 @@ package com.iluwatar.decorator { + fleeBattle() + getAttackPower() : int } - interface Hostile { - + attack() {abstract} - + fleeBattle() {abstract} - + getAttackPower() : int {abstract} - } class SmartHostile { - decorated : Hostile + SmartHostile(decorated : Hostile) diff --git a/delegation/etc/delegation.urm.puml b/delegation/etc/delegation.urm.puml index c143a6ba05cb..378587019bb6 100644 --- a/delegation/etc/delegation.urm.puml +++ b/delegation/etc/delegation.urm.puml @@ -1,19 +1,24 @@ @startuml package com.iluwatar.delegation.simple.printers { - class EpsonPrinter { - + EpsonPrinter() - + print(message : String) - } class HpPrinter { + HpPrinter() + print(message : String) } + class EpsonPrinter { + + EpsonPrinter() + + print(message : String) + } class CanonPrinter { + CanonPrinter() + print(message : String) } } package com.iluwatar.delegation.simple { + class App { + + MESSAGE_TO_PRINT : String {static} + + App() + + main(args : String[]) {static} + } class PrinterController { - printer : Printer + PrinterController(printer : Printer) @@ -22,15 +27,10 @@ package com.iluwatar.delegation.simple { interface Printer { + print(String) {abstract} } - class App { - + MESSAGE_TO_PRINT : String {static} - + App() - + main(args : String[]) {static} - } } PrinterController --> "-printer" Printer +HpPrinter ..|> Printer PrinterController ..|> Printer EpsonPrinter ..|> Printer -HpPrinter ..|> Printer CanonPrinter ..|> Printer @enduml \ No newline at end of file diff --git a/dependency-injection/etc/dependency-injection.urm.puml b/dependency-injection/etc/dependency-injection.urm.puml index c22c658adabe..bf4d105998fa 100644 --- a/dependency-injection/etc/dependency-injection.urm.puml +++ b/dependency-injection/etc/dependency-injection.urm.puml @@ -1,48 +1,48 @@ @startuml package com.iluwatar.dependency.injection { + class AdvancedWizard { + - tobacco : Tobacco + + AdvancedWizard(tobacco : Tobacco) + + smoke() + } interface Wizard { + smoke() {abstract} } - class GuiceWizard { - - tobacco : Tobacco - + GuiceWizard(tobacco : Tobacco) + class RivendellTobacco { + + RivendellTobacco() + } + class SimpleWizard { + - tobacco : OldTobyTobacco + + SimpleWizard() + smoke() } class OldTobyTobacco { + OldTobyTobacco() } - abstract class Tobacco { - + Tobacco() - + smoke(wizard : Wizard) + class SecondBreakfastTobacco { + + SecondBreakfastTobacco() } class App { + App() + main(args : String[]) {static} } - class RivendellTobacco { - + RivendellTobacco() + abstract class Tobacco { + + Tobacco() + + smoke(wizard : Wizard) } - class AdvancedWizard { + class GuiceWizard { - tobacco : Tobacco - + AdvancedWizard(tobacco : Tobacco) - + smoke() - } - class SecondBreakfastTobacco { - + SecondBreakfastTobacco() - } - class SimpleWizard { - - tobacco : OldTobyTobacco - + SimpleWizard() + + GuiceWizard(tobacco : Tobacco) + smoke() } } SimpleWizard --> "-tobacco" OldTobyTobacco AdvancedWizard --> "-tobacco" Tobacco GuiceWizard --> "-tobacco" Tobacco -GuiceWizard ..|> Wizard -OldTobyTobacco --|> Tobacco -RivendellTobacco --|> Tobacco AdvancedWizard ..|> Wizard -SecondBreakfastTobacco --|> Tobacco +RivendellTobacco --|> Tobacco SimpleWizard ..|> Wizard +OldTobyTobacco --|> Tobacco +SecondBreakfastTobacco --|> Tobacco +GuiceWizard ..|> Wizard @enduml \ No newline at end of file diff --git a/double-dispatch/etc/double-dispatch.urm.puml b/double-dispatch/etc/double-dispatch.urm.puml index 725f009c0baf..78d361326fe3 100644 --- a/double-dispatch/etc/double-dispatch.urm.puml +++ b/double-dispatch/etc/double-dispatch.urm.puml @@ -1,17 +1,5 @@ @startuml package com.iluwatar.doubledispatch { - class App { - + App() - + main(args : String[]) {static} - } - class FlamingAsteroid { - + FlamingAsteroid(left : int, top : int, right : int, bottom : int) - + collision(gameObject : GameObject) - } - class SpaceStationIss { - + SpaceStationIss(left : int, top : int, right : int, bottom : int) - + collision(gameObject : GameObject) - } abstract class GameObject { - damaged : boolean - onFire : boolean @@ -27,6 +15,14 @@ package com.iluwatar.doubledispatch { + setOnFire(onFire : boolean) + toString() : String } + class SpaceStationIss { + + SpaceStationIss(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + class FlamingAsteroid { + + FlamingAsteroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } class SpaceStationMir { + SpaceStationMir(left : int, top : int, right : int, bottom : int) + collision(gameObject : GameObject) @@ -56,10 +52,14 @@ package com.iluwatar.doubledispatch { ~ intersectsWith(r : Rectangle) : boolean + toString() : String } + class App { + + App() + + main(args : String[]) {static} + } } -FlamingAsteroid --|> Meteoroid -SpaceStationIss --|> SpaceStationMir GameObject --|> Rectangle +SpaceStationIss --|> SpaceStationMir +FlamingAsteroid --|> Meteoroid SpaceStationMir --|> GameObject Meteoroid --|> GameObject @enduml \ No newline at end of file diff --git a/event-aggregator/etc/event-aggregator.urm.puml b/event-aggregator/etc/event-aggregator.urm.puml index 18b7621c949e..3ccfea536fd4 100644 --- a/event-aggregator/etc/event-aggregator.urm.puml +++ b/event-aggregator/etc/event-aggregator.urm.puml @@ -1,34 +1,10 @@ @startuml package com.iluwatar.event.aggregator { - class KingsHand { - + KingsHand() - + KingsHand(obs : EventObserver) - + onEvent(e : Event) - + timePasses(day : Weekday) - } - class KingJoffrey { - + KingJoffrey() - + onEvent(e : Event) - } - class Scout { - + Scout() - + Scout(obs : EventObserver) - + timePasses(day : Weekday) - } class LordVarys { + LordVarys() + LordVarys(obs : EventObserver) + timePasses(day : Weekday) } - class App { - + App() - + main(args : String[]) {static} - } - class LordBaelish { - + LordBaelish() - + LordBaelish(obs : EventObserver) - + timePasses(day : Weekday) - } abstract class EventEmitter { - observers : List + EventEmitter() @@ -37,9 +13,33 @@ package com.iluwatar.event.aggregator { + registerObserver(obs : EventObserver) + timePasses(Weekday) {abstract} } + class KingJoffrey { + + KingJoffrey() + + onEvent(e : Event) + } + class LordBaelish { + + LordBaelish() + + LordBaelish(obs : EventObserver) + + timePasses(day : Weekday) + } interface EventObserver { + onEvent(Event) {abstract} } + class KingsHand { + + KingsHand() + + KingsHand(obs : EventObserver) + + onEvent(e : Event) + + timePasses(day : Weekday) + } + class Scout { + + Scout() + + Scout(obs : EventObserver) + + timePasses(day : Weekday) + } + class App { + + App() + + main(args : String[]) {static} + } enum Weekday { + FRIDAY {static} + MONDAY {static} @@ -64,10 +64,10 @@ package com.iluwatar.event.aggregator { } } EventEmitter --> "-observers" EventObserver +LordVarys --|> EventEmitter +KingJoffrey ..|> EventObserver +LordBaelish --|> EventEmitter KingsHand ..|> EventObserver KingsHand --|> EventEmitter -KingJoffrey ..|> EventObserver Scout --|> EventEmitter -LordVarys --|> EventEmitter -LordBaelish --|> EventEmitter @enduml \ No newline at end of file diff --git a/event-driven-architecture/etc/event-driven-architecture.urm.puml b/event-driven-architecture/etc/event-driven-architecture.urm.puml index 55039190f101..8314913a6114 100644 --- a/event-driven-architecture/etc/event-driven-architecture.urm.puml +++ b/event-driven-architecture/etc/event-driven-architecture.urm.puml @@ -1,31 +1,34 @@ @startuml package com.iluwatar.eda.handler { - class UserUpdatedEventHandler { - + UserUpdatedEventHandler() - + onEvent(event : UserUpdatedEvent) - } class UserCreatedEventHandler { + UserCreatedEventHandler() + onEvent(event : UserCreatedEvent) } + class UserUpdatedEventHandler { + + UserUpdatedEventHandler() + + onEvent(event : UserUpdatedEvent) + } } package com.iluwatar.eda.event { abstract class AbstractEvent { + AbstractEvent() + getType() : Class } - class UserUpdatedEvent { + class UserCreatedEvent { - user : User - + UserUpdatedEvent(user : User) + + UserCreatedEvent(user : User) + getUser() : User } - class UserCreatedEvent { + class UserUpdatedEvent { - user : User - + UserCreatedEvent(user : User) + + UserUpdatedEvent(user : User) + getUser() : User } } package com.iluwatar.eda.framework { + interface Handler { + + onEvent(E extends Event) {abstract} + } class EventDispatcher { - handlers : Map, Handler> + EventDispatcher() @@ -35,9 +38,6 @@ package com.iluwatar.eda.framework { interface Event { + getType() : Class {abstract} } - interface Handler { - + onEvent(E extends Event) {abstract} - } } package com.iluwatar.eda.model { class User { @@ -52,11 +52,11 @@ package com.iluwatar.eda { + main(args : String[]) {static} } } -UserCreatedEvent --> "-user" User UserUpdatedEvent --> "-user" User +UserCreatedEvent --> "-user" User AbstractEvent ..|> Event -UserUpdatedEventHandler ..|> Handler +UserCreatedEvent --|> AbstractEvent UserCreatedEventHandler ..|> Handler UserUpdatedEvent --|> AbstractEvent -UserCreatedEvent --|> AbstractEvent +UserUpdatedEventHandler ..|> Handler @enduml \ No newline at end of file diff --git a/execute-around/etc/execute-around.urm.puml b/execute-around/etc/execute-around.urm.puml index 66d23ce7ad6e..4fdc4bffb7d2 100644 --- a/execute-around/etc/execute-around.urm.puml +++ b/execute-around/etc/execute-around.urm.puml @@ -1,8 +1,5 @@ @startuml package com.iluwatar.execute.around { - interface FileWriterAction { - + writeFile(FileWriter) {abstract} - } class SimpleFileWriter { + SimpleFileWriter(filename : String, action : FileWriterAction) } @@ -10,5 +7,8 @@ package com.iluwatar.execute.around { + App() + main(args : String[]) {static} } + interface FileWriterAction { + + writeFile(FileWriter) {abstract} + } } @enduml \ No newline at end of file diff --git a/facade/etc/facade.urm.puml b/facade/etc/facade.urm.puml index f72bc2b62468..8c53cb728b26 100644 --- a/facade/etc/facade.urm.puml +++ b/facade/etc/facade.urm.puml @@ -1,7 +1,7 @@ @startuml package com.iluwatar.facade { - class DwarvenTunnelDigger { - + DwarvenTunnelDigger() + class DwarvenGoldDigger { + + DwarvenGoldDigger() + name() : String + work() } @@ -13,15 +13,11 @@ package com.iluwatar.facade { - makeActions(workers : Collection, actions : Action[]) {static} + startNewDay() } - class DwarvenGoldDigger { - + DwarvenGoldDigger() + class DwarvenTunnelDigger { + + DwarvenTunnelDigger() + name() : String + work() } - class App { - + App() - + main(args : String[]) {static} - } abstract class DwarvenMineWorker { + DwarvenMineWorker() - action(action : Action) @@ -33,6 +29,10 @@ package com.iluwatar.facade { + wakeUp() + work() {abstract} } + class App { + + App() + + main(args : String[]) {static} + } class DwarvenCartOperator { + DwarvenCartOperator() + name() : String @@ -51,7 +51,7 @@ package com.iluwatar.facade { DwarvenGoldmineFacade --+ DwarvenMineWorker DwarvenGoldmineFacade --> "-workers" DwarvenMineWorker Action ..+ DwarvenMineWorker -DwarvenTunnelDigger --|> DwarvenMineWorker DwarvenGoldDigger --|> DwarvenMineWorker +DwarvenTunnelDigger --|> DwarvenMineWorker DwarvenCartOperator --|> DwarvenMineWorker @enduml \ No newline at end of file diff --git a/factory-kit/etc/factory-kit.urm.puml b/factory-kit/etc/factory-kit.urm.puml index faf5727eb918..fdee9a01c57f 100644 --- a/factory-kit/etc/factory-kit.urm.puml +++ b/factory-kit/etc/factory-kit.urm.puml @@ -1,34 +1,34 @@ @startuml package com.iluwatar.factorykit { - interface Builder { - + add(WeaponType, Supplier) {abstract} - } class Spear { + Spear() + toString() : String } - class Bow { - + Bow() - + toString() : String - } - class Sword { - + Sword() - + toString() : String - } - interface Weapon { - } class App { + App() + main(args : String[]) {static} } - class Axe { - + Axe() - + toString() : String + interface Weapon { } interface WeaponFactory { + create(WeaponType) : Weapon {abstract} + factory(consumer : Consumer) : WeaponFactory {static} } + class Axe { + + Axe() + + toString() : String + } + class Sword { + + Sword() + + toString() : String + } + class Bow { + + Bow() + + toString() : String + } + interface Builder { + + add(WeaponType, Supplier) {abstract} + } enum WeaponType { + AXE {static} + BOW {static} @@ -39,7 +39,7 @@ package com.iluwatar.factorykit { } } Spear ..|> Weapon -Bow ..|> Weapon -Sword ..|> Weapon Axe ..|> Weapon +Sword ..|> Weapon +Bow ..|> Weapon @enduml \ No newline at end of file diff --git a/factory-method/etc/factory-method.urm.puml b/factory-method/etc/factory-method.urm.puml index ebc9d2ff7ae7..d61984a85047 100644 --- a/factory-method/etc/factory-method.urm.puml +++ b/factory-method/etc/factory-method.urm.puml @@ -17,9 +17,6 @@ package com.iluwatar.factory.method { interface Blacksmith { + manufactureWeapon(WeaponType) : Weapon {abstract} } - interface Weapon { - + getWeaponType() : WeaponType {abstract} - } class ElfWeapon { - weaponType : WeaponType + ElfWeapon(weaponType : WeaponType) @@ -32,6 +29,9 @@ package com.iluwatar.factory.method { + main(args : String[]) {static} - manufactureWeapons() } + interface Weapon { + + getWeaponType() : WeaponType {abstract} + } enum WeaponType { + AXE {static} + SHORT_SWORD {static} diff --git a/feature-toggle/etc/feature-toggle.urm.puml b/feature-toggle/etc/feature-toggle.urm.puml index 762d49cb3ebe..6c935f3e64e5 100644 --- a/feature-toggle/etc/feature-toggle.urm.puml +++ b/feature-toggle/etc/feature-toggle.urm.puml @@ -20,17 +20,17 @@ package com.iluwatar.featuretoggle.user { + isPaid(user : User) : boolean {static} } } -package com.iluwatar.featuretoggle.pattern.propertiesversion { - class PropertiesFeatureToggleVersion { - - isEnhanced : boolean - + PropertiesFeatureToggleVersion(properties : Properties) +package com.iluwatar.featuretoggle.pattern.tieredversion { + class TieredFeatureToggleVersion { + + TieredFeatureToggleVersion() + getWelcomeMessage(user : User) : String + isEnhanced() : boolean } } -package com.iluwatar.featuretoggle.pattern.tieredversion { - class TieredFeatureToggleVersion { - + TieredFeatureToggleVersion() +package com.iluwatar.featuretoggle.pattern.propertiesversion { + class PropertiesFeatureToggleVersion { + - isEnhanced : boolean + + PropertiesFeatureToggleVersion(properties : Properties) + getWelcomeMessage(user : User) : String + isEnhanced() : boolean } @@ -42,6 +42,6 @@ package com.iluwatar.featuretoggle { } } UserGroup --> "-freeGroup" User -TieredFeatureToggleVersion ..|> Service PropertiesFeatureToggleVersion ..|> Service +TieredFeatureToggleVersion ..|> Service @enduml \ No newline at end of file diff --git a/fluentinterface/etc/fluentinterface.urm.puml b/fluentinterface/etc/fluentinterface.urm.puml index 436fcb2e888a..0283e20b594c 100644 --- a/fluentinterface/etc/fluentinterface.urm.puml +++ b/fluentinterface/etc/fluentinterface.urm.puml @@ -31,6 +31,14 @@ package com.iluwatar.fluentinterface.app { } } package com.iluwatar.fluentinterface.fluentiterable.lazy { + abstract class DecoratingIterator { + # fromIterator : Iterator + - next : E + + DecoratingIterator(fromIterator : Iterator) + + computeNext() : E {abstract} + + hasNext() : boolean + + next() : E + } class LazyFluentIterable { - iterable : Iterable # LazyFluentIterable() @@ -45,14 +53,6 @@ package com.iluwatar.fluentinterface.fluentiterable.lazy { + last(count : int) : FluentIterable + map(function : Function) : FluentIterable } - abstract class DecoratingIterator { - # fromIterator : Iterator - - next : E - + DecoratingIterator(fromIterator : Iterator) - + computeNext() : E {abstract} - + hasNext() : boolean - + next() : E - } } package com.iluwatar.fluentinterface.fluentiterable { interface FluentIterable { diff --git a/flux/etc/flux.urm.puml b/flux/etc/flux.urm.puml index e4bece2fc589..a4e0b0622eca 100644 --- a/flux/etc/flux.urm.puml +++ b/flux/etc/flux.urm.puml @@ -1,10 +1,8 @@ @startuml package com.iluwatar.flux.view { - class ContentView { - - content : Content - + ContentView() - + render() - + storeChanged(store : Store) + interface View { + + render() {abstract} + + storeChanged(Store) {abstract} } class MenuView { - selected : MenuItem @@ -13,27 +11,35 @@ package com.iluwatar.flux.view { + render() + storeChanged(store : Store) } - interface View { - + render() {abstract} - + storeChanged(Store) {abstract} + class ContentView { + - content : Content + + ContentView() + + render() + + storeChanged(store : Store) } } package com.iluwatar.flux.action { - class ContentAction { - - content : Content - + ContentAction(content : Content) - + getContent() : Content - } class MenuAction { - menuItem : MenuItem + MenuAction(menuItem : MenuItem) + getMenuItem() : MenuItem } + class ContentAction { + - content : Content + + ContentAction(content : Content) + + getContent() : Content + } abstract class Action { - type : ActionType + Action(type : ActionType) + getType() : ActionType } + enum ActionType { + + CONTENT_CHANGED {static} + + MENU_ITEM_SELECTED {static} + + valueOf(name : String) : ActionType {static} + + values() : ActionType[] {static} + } enum MenuItem { + COMPANY {static} + HOME {static} @@ -51,12 +57,6 @@ package com.iluwatar.flux.action { + valueOf(name : String) : Content {static} + values() : Content[] {static} } - enum ActionType { - + CONTENT_CHANGED {static} - + MENU_ITEM_SELECTED {static} - + valueOf(name : String) : ActionType {static} - + values() : ActionType[] {static} - } } package com.iluwatar.flux.app { class App { @@ -64,14 +64,18 @@ package com.iluwatar.flux.app { + main(args : String[]) {static} } } -package com.iluwatar.flux.store { - abstract class Store { - - views : List - + Store() - # notifyChange() - + onAction(Action) {abstract} - + registerView(view : View) +package com.iluwatar.flux.dispatcher { + class Dispatcher { + - instance : Dispatcher {static} + - stores : List + - Dispatcher() + - dispatchAction(action : Action) + + getInstance() : Dispatcher {static} + + menuItemSelected(menuItem : MenuItem) + + registerStore(store : Store) } +} +package com.iluwatar.flux.store { class ContentStore { - content : Content + ContentStore() @@ -84,16 +88,12 @@ package com.iluwatar.flux.store { + getSelected() : MenuItem + onAction(action : Action) } -} -package com.iluwatar.flux.dispatcher { - class Dispatcher { - - instance : Dispatcher {static} - - stores : List - - Dispatcher() - - dispatchAction(action : Action) - + getInstance() : Dispatcher {static} - + menuItemSelected(menuItem : MenuItem) - + registerStore(store : Store) + abstract class Store { + - views : List + + Store() + # notifyChange() + + onAction(Action) {abstract} + + registerView(view : View) } } MenuAction --> "-menuItem" MenuItem @@ -104,12 +104,12 @@ ContentView --> "-content" Content Dispatcher --> "-stores" Store MenuView --> "-selected" MenuItem Store --> "-views" View -ContentStore --> "-content" Content ContentAction --> "-content" Content -ContentAction --|> Action +ContentStore --> "-content" Content ContentStore --|> Store -ContentView ..|> View MenuAction --|> Action -MenuView ..|> View MenuStore --|> Store +ContentAction --|> Action +MenuView ..|> View +ContentView ..|> View @enduml \ No newline at end of file diff --git a/flyweight/etc/flyweight.urm.puml b/flyweight/etc/flyweight.urm.puml index 98a2b4721683..3f2203712e15 100644 --- a/flyweight/etc/flyweight.urm.puml +++ b/flyweight/etc/flyweight.urm.puml @@ -1,28 +1,17 @@ @startuml package com.iluwatar.flyweight { - class PoisonPotion { - + PoisonPotion() - + drink() - } - class StrengthPotion { - + StrengthPotion() - + drink() - } - class HealingPotion { - + HealingPotion() - + drink() - } class PotionFactory { - potions : Map + PotionFactory() ~ createPotion(type : PotionType) : Potion } - interface Potion { - + drink() {abstract} + class HealingPotion { + + HealingPotion() + + drink() } - class App { - + App() - + main(args : String[]) {static} + class InvisibilityPotion { + + InvisibilityPotion() + + drink() } class AlchemistShop { - bottomShelf : List @@ -33,12 +22,23 @@ package com.iluwatar.flyweight { + getBottomShelf() : List + getTopShelf() : List } - class HolyWaterPotion { - + HolyWaterPotion() + class App { + + App() + + main(args : String[]) {static} + } + interface Potion { + + drink() {abstract} + } + class PoisonPotion { + + PoisonPotion() + drink() } - class InvisibilityPotion { - + InvisibilityPotion() + class StrengthPotion { + + StrengthPotion() + + drink() + } + class HolyWaterPotion { + + HolyWaterPotion() + drink() } enum PotionType { @@ -52,9 +52,9 @@ package com.iluwatar.flyweight { } } AlchemistShop --> "-topShelf" Potion +HealingPotion ..|> Potion +InvisibilityPotion ..|> Potion PoisonPotion ..|> Potion StrengthPotion ..|> Potion -HealingPotion ..|> Potion HolyWaterPotion ..|> Potion -InvisibilityPotion ..|> Potion @enduml \ No newline at end of file diff --git a/front-controller/etc/front-controller.urm.puml b/front-controller/etc/front-controller.urm.puml index 17ccebae133b..8fb9be184488 100644 --- a/front-controller/etc/front-controller.urm.puml +++ b/front-controller/etc/front-controller.urm.puml @@ -1,15 +1,5 @@ @startuml package com.iluwatar.front.controller { - class App { - + App() - + main(args : String[]) {static} - } - class FrontController { - + FrontController() - - getCommand(request : String) : Command - - getCommandClass(request : String) : Class {static} - + handleRequest(request : String) - } class ArcherView { + ArcherView() + display() @@ -17,34 +7,44 @@ package com.iluwatar.front.controller { interface View { + display() {abstract} } - interface Command { - + process() {abstract} - } - class ErrorView { - + ErrorView() + class CatapultView { + + CatapultView() + display() } class ArcherCommand { + ArcherCommand() + process() } - class CatapultView { - + CatapultView() + class App { + + App() + + main(args : String[]) {static} + } + class FrontController { + + FrontController() + - getCommand(request : String) : Command + - getCommandClass(request : String) : Class {static} + + handleRequest(request : String) + } + class UnknownCommand { + + UnknownCommand() + + process() + } + class ErrorView { + + ErrorView() + display() } class CatapultCommand { + CatapultCommand() + process() } - class UnknownCommand { - + UnknownCommand() - + process() + interface Command { + + process() {abstract} } } ArcherView ..|> View -ErrorView ..|> View -ArcherCommand ..|> Command CatapultView ..|> View -CatapultCommand ..|> Command +ArcherCommand ..|> Command UnknownCommand ..|> Command +ErrorView ..|> View +CatapultCommand ..|> Command @enduml \ No newline at end of file diff --git a/half-sync-half-async/etc/half-sync-half-async.urm.puml b/half-sync-half-async/etc/half-sync-half-async.urm.puml index e733dd58659e..88f49eb4da50 100644 --- a/half-sync-half-async/etc/half-sync-half-async.urm.puml +++ b/half-sync-half-async/etc/half-sync-half-async.urm.puml @@ -1,5 +1,18 @@ @startuml package com.iluwatar.halfsynchalfasync { + class AsynchronousService { + - service : ExecutorService + + AsynchronousService(workQueue : BlockingQueue) + + execute(task : AsyncTask) + } + ~class ArithmeticSumTask { + - n : long + + ArithmeticSumTask(n : long) + + call() : Long + + onError(throwable : Throwable) + + onPostCall(result : Long) + + onPreCall() + } class App { + App() - ap(i : long) : long {static} @@ -11,19 +24,6 @@ package com.iluwatar.halfsynchalfasync { + onPostCall(O) {abstract} + onPreCall() {abstract} } - ~class ArithmeticSumTask { - - n : long - + ArithmeticSumTask(n : long) - + call() : Long - + onError(throwable : Throwable) - + onPostCall(result : Long) - + onPreCall() - } - class AsynchronousService { - - service : ExecutorService - + AsynchronousService(workQueue : BlockingQueue) - + execute(task : AsyncTask) - } } ArithmeticSumTask ..+ App ArithmeticSumTask ..|> AsyncTask diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml index c4b0d0a73412..a2f5d04691ec 100644 --- a/hexagonal/etc/hexagonal.urm.puml +++ b/hexagonal/etc/hexagonal.urm.puml @@ -1,31 +1,47 @@ @startuml +package com.iluwatar.hexagonal.sampledata { + class SampleData { + - PLAYERS : List {static} + + SampleData() + - getRandomPlayerDetails() : PlayerDetails {static} + + submitTickets(lotteryService : LotteryService, numTickets : int) {static} + } +} package com.iluwatar.hexagonal.service { - class LotteryServiceImpl { - - bank : WireTransfers - - notifications : LotteryNotifications - - repository : LotteryTicketRepository - + LotteryServiceImpl() - + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult - + submitTicket(ticket : LotteryTicket) : Optional + class ConsoleLottery { + + ConsoleLottery() + + main(args : String[]) {static} + - printMainMenu() {static} + - readString(scanner : Scanner) : String {static} } - interface LotteryService { - + checkTicketForPrize(LotteryTicketId, LotteryNumbers) : LotteryTicketCheckResult {abstract} - + submitTicket(LotteryTicket) : Optional {abstract} +} +package com.iluwatar.hexagonal.mongo { + class MongoConnectionPropertiesLoader { + - DEFAULT_HOST : String {static} + - DEFAULT_PORT : int {static} + + MongoConnectionPropertiesLoader() + + load() {static} } } package com.iluwatar.hexagonal.domain { class LotteryTicketId { - - id : UUID + - id : int + - numAllocated : int {static} + LotteryTicketId() - + getId() : UUID + + LotteryTicketId(id : int) + + equals(o : Object) : boolean + + getId() : int + + hashCode() : int + + toString() : String } - class LotteryConstants { - + PLAYER_MAX_SALDO : int {static} - + PRIZE_AMOUNT : int {static} - + SERVICE_BANK_ACCOUNT : String {static} - + SERVICE_BANK_ACCOUNT_SALDO : int {static} - + TICKET_PRIZE : int {static} - + LotteryConstants() + class LotteryAdministration { + - notifications : LotteryEventLog + - repository : LotteryTicketRepository + - wireTransfers : WireTransfers + + LotteryAdministration(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers) + + getAllSubmittedTickets() : Map + + performLottery() : LotteryNumbers + + resetLottery() } class LotteryNumbers { + MAX_NUMBER : int {static} @@ -39,19 +55,34 @@ package com.iluwatar.hexagonal.domain { + equals(obj : Object) : boolean - generateRandomNumbers() + getNumbers() : Set + + getNumbersAsString() : String + hashCode() : int + + toString() : String + } + class LotteryService { + - notifications : LotteryEventLog + - repository : LotteryTicketRepository + - wireTransfers : WireTransfers + + LotteryService(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers) + + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult + + submitTicket(ticket : LotteryTicket) : Optional + } + -class RandomNumberGenerator { + - randomIterator : OfInt + + RandomNumberGenerator(min : int, max : int) + + nextInt() : int } class PlayerDetails { - bankAccountNumber : String - emailAddress : String - phoneNumber : String - - PlayerDetails(email : String, bankAccount : String, phone : String) - + create(email : String, bankAccount : String, phone : String) : PlayerDetails {static} + + PlayerDetails(email : String, bankAccount : String, phone : String) + equals(obj : Object) : boolean + getBankAccount() : String + getEmail() : String + getPhoneNumber() : String + hashCode() : int + + toString() : String } class LotteryTicketCheckResult { - checkResult : CheckResult @@ -63,20 +94,30 @@ package com.iluwatar.hexagonal.domain { + getResult() : CheckResult + hashCode() : int } + class LotteryConstants { + + PLAYER_MAX_SALDO : int {static} + + PRIZE_AMOUNT : int {static} + + SERVICE_BANK_ACCOUNT : String {static} + + SERVICE_BANK_ACCOUNT_SALDO : int {static} + + TICKET_PRIZE : int {static} + - LotteryConstants() + } class LotteryTicket { + - id : LotteryTicketId - lotteryNumbers : LotteryNumbers - playerDetails : PlayerDetails - - LotteryTicket(details : PlayerDetails, numbers : LotteryNumbers) - + create(details : PlayerDetails, numbers : LotteryNumbers) : LotteryTicket {static} + + LotteryTicket(id : LotteryTicketId, details : PlayerDetails, numbers : LotteryNumbers) + equals(obj : Object) : boolean + + getId() : LotteryTicketId + getNumbers() : LotteryNumbers + getPlayerDetails() : PlayerDetails + hashCode() : int + + setId(id : LotteryTicketId) + + toString() : String } - -class RandomNumberGenerator { - - randomIterator : OfInt - + RandomNumberGenerator(min : int, max : int) - + nextInt() : int + class LotteryUtils { + - LotteryUtils() + + checkTicketForPrize(repository : LotteryTicketRepository, id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult {static} } enum CheckResult { + NO_PRIZE {static} @@ -87,23 +128,64 @@ package com.iluwatar.hexagonal.domain { } } package com.iluwatar.hexagonal.banking { - class WireTransfersImpl { - - accounts : Map {static} - + WireTransfersImpl() - + getFunds(bankAccount : String) : int - + setFunds(bankAccount : String, amount : int) - + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean - } interface WireTransfers { + getFunds(String) : int {abstract} + setFunds(String, int) {abstract} + transferFunds(int, String, String) : boolean {abstract} } + class MongoBank { + - DEFAULT_ACCOUNTS_COLLECTION : String {static} + - DEFAULT_DB : String {static} + - accountsCollection : MongoCollection + - database : MongoDatabase + - mongoClient : MongoClient + + MongoBank() + + MongoBank(dbName : String, accountsCollectionName : String) + + connect() + + connect(dbName : String, accountsCollectionName : String) + + getAccountsCollection() : MongoCollection + + getFunds(bankAccount : String) : int + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean + } + class InMemoryBank { + - accounts : Map {static} + + InMemoryBank() + + getFunds(bankAccount : String) : int + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean + } } package com.iluwatar.hexagonal.database { - class LotteryTicketInMemoryRepository { + class MongoTicketRepository { + - DEFAULT_COUNTERS_COLLECTION : String {static} + - DEFAULT_DB : String {static} + - DEFAULT_TICKETS_COLLECTION : String {static} + - countersCollection : MongoCollection + - database : MongoDatabase + - mongoClient : MongoClient + - ticketsCollection : MongoCollection + + MongoTicketRepository() + + MongoTicketRepository(dbName : String, ticketsCollectionName : String, countersCollectionName : String) + + connect() + + connect(dbName : String, ticketsCollectionName : String, countersCollectionName : String) + + deleteAll() + - docToTicket(doc : Document) : LotteryTicket + + findAll() : Map + + findById(id : LotteryTicketId) : Optional + + getCountersCollection() : MongoCollection + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + getNextId() : int + + getTicketsCollection() : MongoCollection + - initCounters() + + save(ticket : LotteryTicket) : Optional + } + class InMemoryTicketRepository { - tickets : Map {static} - + LotteryTicketInMemoryRepository() + + InMemoryTicketRepository() + deleteAll() + findAll() : Map + findById(id : LotteryTicketId) : Optional @@ -116,68 +198,79 @@ package com.iluwatar.hexagonal.database { + save(LotteryTicket) : Optional {abstract} } } -package com.iluwatar.hexagonal.eventlog { - interface LotteryNotifications { - + notifyNoWin(PlayerDetails) {abstract} - + notifyPrize(PlayerDetails, int) {abstract} - + notifyPrizeError(PlayerDetails, int) {abstract} - + notifyTicketSubmitError(PlayerDetails) {abstract} - + notifyTicketSubmitted(PlayerDetails) {abstract} - } - class LotteryNotificationsImpl { - + LotteryNotificationsImpl() - + notifyNoWin(details : PlayerDetails) - + notifyPrize(details : PlayerDetails, prizeAmount : int) - + notifyPrizeError(details : PlayerDetails, prizeAmount : int) - + notifyTicketSubmitError(details : PlayerDetails) - + notifyTicketSubmitted(details : PlayerDetails) - } -} package com.iluwatar.hexagonal { class App { - - allPlayerDetails : List {static} + App() - - getRandomPlayerDetails() : PlayerDetails {static} + main(args : String[]) {static} - - submitTickets(lotteryService : LotteryService, numTickets : int) {static} } } package com.iluwatar.hexagonal.administration { - interface LotteryAdministration { - + getAllSubmittedTickets() : Map {abstract} - + performLottery() : LotteryNumbers {abstract} - + resetLottery() {abstract} - } - class LotteryAdministrationImpl { - - bank : WireTransfers - - notifications : LotteryNotifications - - repository : LotteryTicketRepository - - service : LotteryService - + LotteryAdministrationImpl() - + getAllSubmittedTickets() : Map - + performLottery() : LotteryNumbers - + resetLottery() + class ConsoleAdministration { + + ConsoleAdministration() + + main(args : String[]) {static} + - printMainMenu() {static} + - readString(scanner : Scanner) : String {static} + } +} +package com.iluwatar.hexagonal.eventlog { + interface LotteryEventLog { + + prizeError(PlayerDetails, int) {abstract} + + ticketDidNotWin(PlayerDetails) {abstract} + + ticketSubmitError(PlayerDetails) {abstract} + + ticketSubmitted(PlayerDetails) {abstract} + + ticketWon(PlayerDetails, int) {abstract} + } + class StdOutEventLog { + + StdOutEventLog() + + prizeError(details : PlayerDetails, prizeAmount : int) + + ticketDidNotWin(details : PlayerDetails) + + ticketSubmitError(details : PlayerDetails) + + ticketSubmitted(details : PlayerDetails) + + ticketWon(details : PlayerDetails, prizeAmount : int) + } + class MongoEventLog { + - DEFAULT_DB : String {static} + - DEFAULT_EVENTS_COLLECTION : String {static} + - database : MongoDatabase + - eventsCollection : MongoCollection + - mongoClient : MongoClient + - stdOutEventLog : StdOutEventLog + + MongoEventLog() + + MongoEventLog(dbName : String, eventsCollectionName : String) + + connect() + + connect(dbName : String, eventsCollectionName : String) + + getEventsCollection() : MongoCollection + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + prizeError(details : PlayerDetails, prizeAmount : int) + + ticketDidNotWin(details : PlayerDetails) + + ticketSubmitError(details : PlayerDetails) + + ticketSubmitted(details : PlayerDetails) + + ticketWon(details : PlayerDetails, prizeAmount : int) } } +LotteryAdministration --+ LotteryTicketCheckResult LotteryTicket --> "-playerDetails" PlayerDetails -LotteryAdministrationImpl --> "-bank" WireTransfers -App --> "-allPlayerDetails" PlayerDetails +MongoEventLog --> "-stdOutEventLog" StdOutEventLog +LotteryService --> "-wireTransfers" WireTransfers +LotteryAdministration --> "-notifications" LotteryEventLog RandomNumberGenerator ..+ PrimitiveIterator -LotteryAdministrationImpl --> "-repository" LotteryTicketRepository -LotteryAdministrationImpl --+ LotteryTicketCheckResult -LotteryServiceImpl --> "-notifications" LotteryNotifications +LotteryAdministration --> "-wireTransfers" WireTransfers +LotteryTicket --> "-id" LotteryTicketId +LotteryService --> "-notifications" LotteryEventLog +LotteryAdministration --> "-repository" LotteryTicketRepository LotteryTicket --> "-lotteryNumbers" LotteryNumbers -LotteryAdministrationImpl --> "-notifications" LotteryNotifications -LotteryServiceImpl --> "-repository" LotteryTicketRepository -LotteryServiceImpl --+ LotteryTicketCheckResult -LotteryServiceImpl --> "-bank" WireTransfers +SampleData --> "-PLAYERS" PlayerDetails +ConsoleLottery --+ LotteryTicketCheckResult RandomNumberGenerator ..+ LotteryNumbers -LotteryAdministrationImpl --> "-service" LotteryService +LotteryService --> "-repository" LotteryTicketRepository +LotteryUtils --+ LotteryTicketCheckResult LotteryTicketCheckResult --> "-checkResult" CheckResult CheckResult ..+ LotteryTicketCheckResult -LotteryTicketInMemoryRepository ..|> LotteryTicketRepository -WireTransfersImpl ..|> WireTransfers -LotteryServiceImpl ..|> LotteryService -LotteryNotificationsImpl ..|> LotteryNotifications -LotteryAdministrationImpl ..|> LotteryAdministration +MongoTicketRepository ..|> LotteryTicketRepository +MongoBank ..|> WireTransfers +InMemoryBank ..|> WireTransfers +StdOutEventLog ..|> LotteryEventLog +InMemoryTicketRepository ..|> LotteryTicketRepository +MongoEventLog ..|> LotteryEventLog @enduml \ No newline at end of file diff --git a/intercepting-filter/etc/intercepting-filter.urm.puml b/intercepting-filter/etc/intercepting-filter.urm.puml index f5bfb54e4214..e3616c3abf64 100644 --- a/intercepting-filter/etc/intercepting-filter.urm.puml +++ b/intercepting-filter/etc/intercepting-filter.urm.puml @@ -1,10 +1,16 @@ @startuml package com.iluwatar.intercepting.filter { - interface Filter { - + execute(Order) : String {abstract} - + getLast() : Filter {abstract} - + getNext() : Filter {abstract} - + setNext(Filter) {abstract} + class DepositFilter { + + DepositFilter() + + execute(order : Order) : String + } + class AddressFilter { + + AddressFilter() + + execute(order : Order) : String + } + class App { + + App() + + main(args : String[]) {static} } abstract class AbstractFilter { - next : Filter @@ -15,14 +21,6 @@ package com.iluwatar.intercepting.filter { + getNext() : Filter + setNext(filter : Filter) } - class ContactFilter { - + ContactFilter() - + execute(order : Order) : String - } - class OrderFilter { - + OrderFilter() - + execute(order : Order) : String - } class Order { - address : String - contactNumber : String @@ -42,36 +40,38 @@ package com.iluwatar.intercepting.filter { + setName(name : String) + setOrder(order : String) } - class AddressFilter { - + AddressFilter() - + execute(order : Order) : String - } - ~class DListener { - ~ DListener(this$0 : Target) - + actionPerformed(e : ActionEvent) - } class FilterManager { - filterChain : FilterChain + FilterManager() + addFilter(filter : Filter) + filterRequest(order : Order) : String } - class FilterChain { - - chain : Filter - + FilterChain() - + addFilter(filter : Filter) + class NameFilter { + + NameFilter() + execute(order : Order) : String } - class DepositFilter { - + DepositFilter() + class ContactFilter { + + ContactFilter() + execute(order : Order) : String } - class App { - + App() - + main(args : String[]) {static} + interface Filter { + + execute(Order) : String {abstract} + + getLast() : Filter {abstract} + + getNext() : Filter {abstract} + + setNext(Filter) {abstract} } - class NameFilter { - + NameFilter() + ~class DListener { + ~ DListener() + + actionPerformed(e : ActionEvent) + } + class OrderFilter { + + OrderFilter() + + execute(order : Order) : String + } + class FilterChain { + - chain : Filter + + FilterChain() + + addFilter(filter : Filter) + execute(order : Order) : String } } @@ -79,10 +79,10 @@ AbstractFilter --> "-next" Filter DListener --+ Target FilterChain --> "-chain" Filter FilterManager --> "-filterChain" FilterChain +DepositFilter --|> AbstractFilter +AddressFilter --|> AbstractFilter AbstractFilter ..|> Filter +NameFilter --|> AbstractFilter ContactFilter --|> AbstractFilter OrderFilter --|> AbstractFilter -AddressFilter --|> AbstractFilter -DepositFilter --|> AbstractFilter -NameFilter --|> AbstractFilter @enduml \ No newline at end of file diff --git a/interpreter/etc/interpreter.urm.puml b/interpreter/etc/interpreter.urm.puml index bdbd369d63b2..dc0d83bf2fb1 100644 --- a/interpreter/etc/interpreter.urm.puml +++ b/interpreter/etc/interpreter.urm.puml @@ -1,14 +1,16 @@ @startuml package com.iluwatar.interpreter { - abstract class Expression { - + Expression() - + interpret() : int {abstract} - + toString() : String {abstract} + class NumberExpression { + - number : int + + NumberExpression(number : int) + + NumberExpression(s : String) + + interpret() : int + + toString() : String } - class PlusExpression { + class MinusExpression { - leftExpression : Expression - rightExpression : Expression - + PlusExpression(leftExpression : Expression, rightExpression : Expression) + + MinusExpression(leftExpression : Expression, rightExpression : Expression) + interpret() : int + toString() : String } @@ -18,12 +20,10 @@ package com.iluwatar.interpreter { + isOperator(s : String) : boolean {static} + main(args : String[]) {static} } - class NumberExpression { - - number : int - + NumberExpression(number : int) - + NumberExpression(s : String) - + interpret() : int - + toString() : String + abstract class Expression { + + Expression() + + interpret() : int {abstract} + + toString() : String {abstract} } class MultiplyExpression { - leftExpression : Expression @@ -32,10 +32,10 @@ package com.iluwatar.interpreter { + interpret() : int + toString() : String } - class MinusExpression { + class PlusExpression { - leftExpression : Expression - rightExpression : Expression - + MinusExpression(leftExpression : Expression, rightExpression : Expression) + + PlusExpression(leftExpression : Expression, rightExpression : Expression) + interpret() : int + toString() : String } @@ -43,8 +43,8 @@ package com.iluwatar.interpreter { MultiplyExpression --> "-leftExpression" Expression MinusExpression --> "-leftExpression" Expression PlusExpression --> "-leftExpression" Expression -PlusExpression --|> Expression NumberExpression --|> Expression -MultiplyExpression --|> Expression MinusExpression --|> Expression +MultiplyExpression --|> Expression +PlusExpression --|> Expression @enduml \ No newline at end of file diff --git a/iterator/etc/iterator.urm.puml b/iterator/etc/iterator.urm.puml index cbafd65959f5..032cd4c165b4 100644 --- a/iterator/etc/iterator.urm.puml +++ b/iterator/etc/iterator.urm.puml @@ -1,13 +1,17 @@ @startuml package com.iluwatar.iterator { - interface ItemIterator { - + hasNext() : boolean {abstract} - + next() : Item {abstract} - } class App { + App() + main(args : String[]) {static} } + class Item { + - name : String + - type : ItemType + + Item(type : ItemType, name : String) + + getType() : ItemType + + setType(type : ItemType) + + toString() : String + } class TreasureChestItemIterator { - chest : TreasureChest - idx : int @@ -23,13 +27,9 @@ package com.iluwatar.iterator { + getItems() : List ~ iterator(itemType : ItemType) : ItemIterator } - class Item { - - name : String - - type : ItemType - + Item(type : ItemType, name : String) - + getType() : ItemType - + setType(type : ItemType) - + toString() : String + interface ItemIterator { + + hasNext() : boolean {abstract} + + next() : Item {abstract} } enum ItemType { + ANY {static} diff --git a/layers/etc/layers.urm.puml b/layers/etc/layers.urm.puml index d67216ff8885..2ec6e142eabd 100644 --- a/layers/etc/layers.urm.puml +++ b/layers/etc/layers.urm.puml @@ -3,6 +3,44 @@ package com.iluwatar.layers { interface View { + render() {abstract} } + class CakeToppingInfo { + + calories : int + + id : Optional + + name : String + + CakeToppingInfo(id : Long, name : String, calories : int) + + CakeToppingInfo(name : String, calories : int) + + toString() : String + } + class CakeLayer { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeLayer() + + CakeLayer(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } + class CakeLayerInfo { + + calories : int + + id : Optional + + name : String + + CakeLayerInfo(id : Long, name : String, calories : int) + + CakeLayerInfo(name : String, calories : int) + + toString() : String + } + class CakeViewImpl { + - cakeBakingService : CakeBakingService + + CakeViewImpl(cakeBakingService : CakeBakingService) + + render() + } class CakeBakingServiceImpl { - context : AbstractApplicationContext + CakeBakingServiceImpl() @@ -15,8 +53,6 @@ package com.iluwatar.layers { + saveNewLayer(layerInfo : CakeLayerInfo) + saveNewTopping(toppingInfo : CakeToppingInfo) } - interface CakeDao { - } class CakeTopping { - cake : Cake - calories : int @@ -34,16 +70,20 @@ package com.iluwatar.layers { + setName(name : String) + toString() : String } - class CakeLayerInfo { - + calories : int - + id : Optional - + name : String - + CakeLayerInfo(id : Long, name : String, calories : int) - + CakeLayerInfo(name : String, calories : int) + class Cake { + - id : Long + - layers : Set + - topping : CakeTopping + + Cake() + + addLayer(layer : CakeLayer) + + getId() : Long + + getLayers() : Set + + getTopping() : CakeTopping + + setId(id : Long) + + setLayers(layers : Set) + + setTopping(topping : CakeTopping) + toString() : String } - interface CakeLayerDao { - } interface CakeToppingDao { } interface CakeBakingService { @@ -54,18 +94,15 @@ package com.iluwatar.layers { + saveNewLayer(CakeLayerInfo) {abstract} + saveNewTopping(CakeToppingInfo) {abstract} } - class CakeViewImpl { - - cakeBakingService : CakeBakingService - + CakeViewImpl(cakeBakingService : CakeBakingService) - + render() + class App { + - cakeBakingService : CakeBakingService {static} + + App() + - initializeData(cakeBakingService : CakeBakingService) {static} + + main(args : String[]) {static} } - class CakeToppingInfo { - + calories : int - + id : Optional - + name : String - + CakeToppingInfo(id : Long, name : String, calories : int) - + CakeToppingInfo(name : String, calories : int) - + toString() : String + interface CakeDao { + } + interface CakeLayerDao { } class CakeInfo { + cakeLayerInfos : List @@ -76,43 +113,6 @@ package com.iluwatar.layers { + calculateTotalCalories() : int + toString() : String } - class App { - - cakeBakingService : CakeBakingService {static} - + App() - - initializeData(cakeBakingService : CakeBakingService) {static} - + main(args : String[]) {static} - } - class Cake { - - id : Long - - layers : Set - - topping : CakeTopping - + Cake() - + addLayer(layer : CakeLayer) - + getId() : Long - + getLayers() : Set - + getTopping() : CakeTopping - + setId(id : Long) - + setLayers(layers : Set) - + setTopping(topping : CakeTopping) - + toString() : String - } - class CakeLayer { - - cake : Cake - - calories : int - - id : Long - - name : String - + CakeLayer() - + CakeLayer(name : String, calories : int) - + getCake() : Cake - + getCalories() : int - + getId() : Long - + getName() : String - + setCake(cake : Cake) - + setCalories(calories : int) - + setId(id : Long) - + setName(name : String) - + toString() : String - } } CakeViewImpl --> "-cakeBakingService" CakeBakingService CakeInfo --> "-cakeToppingInfo" CakeToppingInfo @@ -120,6 +120,6 @@ CakeInfo --> "-cakeLayerInfos" CakeLayerInfo App --> "-cakeBakingService" CakeBakingService CakeLayer --> "-cake" Cake Cake --> "-topping" CakeTopping -CakeBakingServiceImpl ..|> CakeBakingService CakeViewImpl ..|> View +CakeBakingServiceImpl ..|> CakeBakingService @enduml \ No newline at end of file diff --git a/lazy-loading/etc/lazy-loading.urm.puml b/lazy-loading/etc/lazy-loading.urm.puml index 96c42755350e..80192160a425 100644 --- a/lazy-loading/etc/lazy-loading.urm.puml +++ b/lazy-loading/etc/lazy-loading.urm.puml @@ -2,20 +2,24 @@ package com.iluwatar.lazy.loading { ~class HeavyFactory { - heavyInstance : Heavy - ~ HeavyFactory(this$0 : Java8Holder) + ~ HeavyFactory() + get() : Heavy } - class HolderNaive { + class App { + + App() + + main(args : String[]) {static} + } + class HolderThreadSafe { - heavy : Heavy - + HolderNaive() + + HolderThreadSafe() + getHeavy() : Heavy } class Heavy { + Heavy() } - class HolderThreadSafe { + class HolderNaive { - heavy : Heavy - + HolderThreadSafe() + + HolderNaive() + getHeavy() : Heavy } class Java8Holder { @@ -24,12 +28,8 @@ package com.iluwatar.lazy.loading { - createAndCacheHeavy() : Heavy + getHeavy() : Heavy } - class App { - + App() - + main(args : String[]) {static} - } } -HolderThreadSafe --> "-heavy" Heavy HolderNaive --> "-heavy" Heavy +HolderThreadSafe --> "-heavy" Heavy HeavyFactory --> "-heavyInstance" Heavy @enduml \ No newline at end of file diff --git a/mediator/etc/mediator.urm.puml b/mediator/etc/mediator.urm.puml index 0b3baab5a894..330e024abb1b 100644 --- a/mediator/etc/mediator.urm.puml +++ b/mediator/etc/mediator.urm.puml @@ -1,24 +1,20 @@ @startuml package com.iluwatar.mediator { - class App { - + App() - + main(args : String[]) {static} - } - class Hobbit { - + Hobbit() - + toString() : String + interface Party { + + act(PartyMember, Action) {abstract} + + addMember(PartyMember) {abstract} } interface PartyMember { + act(Action) {abstract} + joinedParty(Party) {abstract} + partyAction(Action) {abstract} } - interface Party { - + act(PartyMember, Action) {abstract} - + addMember(PartyMember) {abstract} + class Rogue { + + Rogue() + + toString() : String } - class Wizard { - + Wizard() + class Hunter { + + Hunter() + toString() : String } class PartyImpl { @@ -27,12 +23,12 @@ package com.iluwatar.mediator { + act(actor : PartyMember, action : Action) + addMember(member : PartyMember) } - class Hunter { - + Hunter() + class Hobbit { + + Hobbit() + toString() : String } - class Rogue { - + Rogue() + class Wizard { + + Wizard() + toString() : String } abstract class PartyMemberBase { @@ -43,6 +39,10 @@ package com.iluwatar.mediator { + partyAction(action : Action) + toString() : String {abstract} } + class App { + + App() + + main(args : String[]) {static} + } enum Action { + ENEMY {static} + GOLD {static} @@ -59,10 +59,10 @@ package com.iluwatar.mediator { } PartyImpl --> "-members" PartyMember PartyMemberBase --> "-party" Party +Rogue --|> PartyMemberBase +Hunter --|> PartyMemberBase +PartyImpl ..|> Party Hobbit --|> PartyMemberBase Wizard --|> PartyMemberBase -PartyImpl ..|> Party -Hunter --|> PartyMemberBase -Rogue --|> PartyMemberBase PartyMemberBase ..|> PartyMember @enduml \ No newline at end of file diff --git a/memento/etc/memento.urm.puml b/memento/etc/memento.urm.puml index aa63b4ebef65..316d4047ddbd 100644 --- a/memento/etc/memento.urm.puml +++ b/memento/etc/memento.urm.puml @@ -1,17 +1,5 @@ @startuml package com.iluwatar.memento { - class Star { - - ageYears : int - - massTons : int - - type : StarType - + Star(startType : StarType, startAge : int, startMass : int) - ~ getMemento() : StarMemento - ~ setMemento(memento : StarMemento) - + timePasses() - + toString() : String - } - interface StarMemento { - } -class StarMementoInternal { - ageYears : int - massTons : int @@ -28,6 +16,18 @@ package com.iluwatar.memento { + App() + main(args : String[]) {static} } + interface StarMemento { + } + class Star { + - ageYears : int + - massTons : int + - type : StarType + + Star(startType : StarType, startAge : int, startMass : int) + ~ getMemento() : StarMemento + ~ setMemento(memento : StarMemento) + + timePasses() + + toString() : String + } enum StarType { + DEAD {static} + RED_GIANT {static} diff --git a/model-view-controller/etc/model-view-controller.urm.puml b/model-view-controller/etc/model-view-controller.urm.puml index f8137bdae033..ad979fe0e066 100644 --- a/model-view-controller/etc/model-view-controller.urm.puml +++ b/model-view-controller/etc/model-view-controller.urm.puml @@ -13,14 +13,6 @@ package com.iluwatar.model.view.controller { + setNourishment(nourishment : Nourishment) + toString() : String } - class App { - + App() - + main(args : String[]) {static} - } - class GiantView { - + GiantView() - + displayGiant(giant : GiantModel) - } class GiantController { - giant : GiantModel - view : GiantView @@ -33,6 +25,23 @@ package com.iluwatar.model.view.controller { + setNourishment(nourishment : Nourishment) + updateView() } + class GiantView { + + GiantView() + + displayGiant(giant : GiantModel) + } + class App { + + App() + + main(args : String[]) {static} + } + enum Health { + + DEAD {static} + + HEALTHY {static} + + WOUNDED {static} + - title : String + + toString() : String + + valueOf(name : String) : Health {static} + + values() : Health[] {static} + } enum Nourishment { + HUNGRY {static} + SATURATED {static} @@ -51,15 +60,6 @@ package com.iluwatar.model.view.controller { + valueOf(name : String) : Fatigue {static} + values() : Fatigue[] {static} } - enum Health { - + DEAD {static} - + HEALTHY {static} - + WOUNDED {static} - - title : String - + toString() : String - + valueOf(name : String) : Health {static} - + values() : Health[] {static} - } } GiantModel --> "-nourishment" Nourishment GiantController --> "-giant" GiantModel diff --git a/model-view-presenter/etc/model-view-presenter.urm.puml b/model-view-presenter/etc/model-view-presenter.urm.puml index 64bcfba32dd1..0cec653e44c1 100644 --- a/model-view-presenter/etc/model-view-presenter.urm.puml +++ b/model-view-presenter/etc/model-view-presenter.urm.puml @@ -1,42 +1,5 @@ @startuml package com.iluwatar.model.view.presenter { - class FileLoader { - - fileName : String - - loaded : boolean - + FileLoader() - + fileExists() : boolean - + getFileName() : String - + isLoaded() : boolean - + loadData() : String - + setFileName(fileName : String) - } - class FileSelectorJFrame { - - area : JTextArea - - cancel : JButton - - contents : JLabel - - fileName : String - - info : JLabel - - input : JTextField - - ok : JButton - - panel : JPanel - - presenter : FileSelectorPresenter - - serialVersionUID : long {static} - + FileSelectorJFrame() - + actionPerformed(e : ActionEvent) - + close() - + displayData(data : String) - + getFileName() : String - + getPresenter() : FileSelectorPresenter - + isOpened() : boolean - + open() - + setFileName(name : String) - + setPresenter(presenter : FileSelectorPresenter) - + showMessage(message : String) - } - class App { - + App() - + main(args : String[]) {static} - } interface FileSelectorView { + close() {abstract} + displayData(String) {abstract} @@ -67,6 +30,16 @@ package com.iluwatar.model.view.presenter { + setPresenter(presenter : FileSelectorPresenter) + showMessage(message : String) } + class FileLoader { + - fileName : String + - loaded : boolean + + FileLoader() + + fileExists() : boolean + + getFileName() : String + + isLoaded() : boolean + + loadData() : String + + setFileName(fileName : String) + } class FileSelectorPresenter { - loader : FileLoader - view : FileSelectorView @@ -77,11 +50,38 @@ package com.iluwatar.model.view.presenter { + setLoader(loader : FileLoader) + start() } + class App { + + App() + + main(args : String[]) {static} + } + class FileSelectorJFrame { + - area : JTextArea + - cancel : JButton + - contents : JLabel + - fileName : String + - info : JLabel + - input : JTextField + - ok : JButton + - panel : JPanel + - presenter : FileSelectorPresenter + - serialVersionUID : long {static} + + FileSelectorJFrame() + + actionPerformed(e : ActionEvent) + + close() + + displayData(data : String) + + getFileName() : String + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } } -FileSelectorStub --> "-presenter" FileSelectorPresenter FileSelectorJFrame --> "-presenter" FileSelectorPresenter +FileSelectorStub --> "-presenter" FileSelectorPresenter FileSelectorPresenter --> "-loader" FileLoader FileSelectorPresenter --> "-view" FileSelectorView -FileSelectorJFrame ..|> FileSelectorView FileSelectorStub ..|> FileSelectorView +FileSelectorJFrame ..|> FileSelectorView @enduml \ No newline at end of file diff --git a/monostate/etc/monostate.urm.puml b/monostate/etc/monostate.urm.puml index 3c09bb4ed57e..6574bcad1f0e 100644 --- a/monostate/etc/monostate.urm.puml +++ b/monostate/etc/monostate.urm.puml @@ -1,5 +1,13 @@ @startuml package com.iluwatar.monostate { + class App { + + App() + + main(args : String[]) {static} + } + class Request { + + value : String + + Request(value : String) + } class LoadBalancer { - id : int {static} - lastServedId : int {static} @@ -10,14 +18,6 @@ package com.iluwatar.monostate { + getNoOfServers() : int + serverRequest(request : Request) } - class App { - + App() - + main(args : String[]) {static} - } - class Request { - + value : String - + Request(value : String) - } class Server { + host : String + id : int diff --git a/mute-idiom/etc/mute-idiom.urm.puml b/mute-idiom/etc/mute-idiom.urm.puml index d4efc2db120e..c992773f52c1 100644 --- a/mute-idiom/etc/mute-idiom.urm.puml +++ b/mute-idiom/etc/mute-idiom.urm.puml @@ -1,6 +1,7 @@ @startuml package com.iluwatar.mute { - interface Resource { + interface CheckedRunnable { + + run() {abstract} } class App { + App() @@ -16,8 +17,7 @@ package com.iluwatar.mute { + loggedMute(runnable : CheckedRunnable) {static} + mute(runnable : CheckedRunnable) {static} } - interface CheckedRunnable { - + run() {abstract} + interface Resource { } } @enduml \ No newline at end of file diff --git a/mutex/etc/mutex.urm.puml b/mutex/etc/mutex.urm.puml index 24ea8363088e..68b96d66816a 100644 --- a/mutex/etc/mutex.urm.puml +++ b/mutex/etc/mutex.urm.puml @@ -1,5 +1,11 @@ @startuml package com.iluwatar.mutex { + class Jar { + - beans : int + - lock : Lock + + Jar(beans : int, lock : Lock) + + takeBean() : boolean + } interface Lock { + acquire() {abstract} + release() {abstract} @@ -11,12 +17,6 @@ package com.iluwatar.mutex { + getOwner() : Object + release() } - class Jar { - - beans : int - - lock : Lock - + Jar(beans : int, lock : Lock) - + takeBean() : boolean - } class App { + App() + main(args : String[]) {static} diff --git a/naked-objects/etc/naked-objects-dom.urm.puml b/naked-objects/etc/naked-objects-dom.urm.puml index ea32b57874a4..6662a560a683 100644 --- a/naked-objects/etc/naked-objects-dom.urm.puml +++ b/naked-objects/etc/naked-objects-dom.urm.puml @@ -23,58 +23,13 @@ package domainapp.dom.modules.simple { } class SimpleObject { - container : DomainObjectContainer - - dnFieldFlags : byte[] {static} - - dnFieldNames : String[] {static} - - dnFieldTypes : Class[] {static} - # dnFlags : byte - - dnInheritedFieldCount : int {static} - - dnPersistableSuperclass : Class {static} - # dnStateManager : StateManager - name : String + SimpleObject() - + ___dn$loadClass(className : String) : Class {static} - - __dnFieldFlagsInit() : byte[] {static} - - __dnFieldNamesInit() : String[] {static} - - __dnFieldTypesInit() : Class[] {static} - # __dnGetInheritedFieldCount() : int {static} - - __dnPersistableSuperclassInit() : Class {static} + compareTo(other : SimpleObject) : int + default0UpdateName() : String - # dnCopyField(obj : SimpleObject, index : int) - + dnCopyFields(obj : Object, indices : int[]) - + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) - # dnCopyKeyFieldsFromObjectId(oid : Object) - + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) - + dnCopyKeyFieldsToObjectId(oid : Object) - + dnGetExecutionContext() : ExecutionContextReference - # dnGetManagedFieldCount() : int {static} - + dnGetObjectId() : Object - + dnGetTransactionalObjectId() : Object - + dnGetVersion() : Object - + dnGetname() : String - + dnIsDeleted() : boolean - + dnIsDetached() : boolean - + dnIsDirty() : boolean - + dnIsNew() : boolean - + dnIsPersistent() : boolean - + dnIsTransactional() : boolean - + dnMakeDirty(fieldName : String) - + dnNewInstance(sm : StateManager) : Persistable - + dnNewInstance(sm : StateManager, obj : Object) : Persistable - + dnNewObjectIdInstance() : Object - + dnNewObjectIdInstance(key : Object) : Object - # dnPreSerialize() - + dnProvideField(index : int) - + dnProvideFields(indices : int[]) - + dnReplaceField(index : int) - + dnReplaceFields(indices : int[]) - + dnReplaceFlags() - + dnReplaceStateManager(sm : StateManager) - + dnSetname(name : String) - - dnSuperClone() : Object + getName() : String + getVersionSequence() : Long - + setName(val : String) + + setName(name : String) + title() : TranslatableString + updateName(name : String) : SimpleObject + validateUpdateName(name : String) : TranslatableString diff --git a/naked-objects/etc/naked-objects-fixture.urm.puml b/naked-objects/etc/naked-objects-fixture.urm.puml index 65c44410ae92..21e38d7102ac 100644 --- a/naked-objects/etc/naked-objects-fixture.urm.puml +++ b/naked-objects/etc/naked-objects-fixture.urm.puml @@ -1,16 +1,16 @@ @startuml package domainapp.dom.app.homepage { + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } class HomePageViewModel { ~ simpleObjects : SimpleObjects + HomePageViewModel() + getObjects() : List + title() : String } - class HomePageService { - ~ container : DomainObjectContainer - + HomePageService() - + homePage() : HomePageViewModel - } } package domainapp.dom.modules.simple { class SimpleObject { diff --git a/naked-objects/etc/naked-objects-integtests.urm.puml b/naked-objects/etc/naked-objects-integtests.urm.puml index 1f2dfc4c2321..65c44410ae92 100644 --- a/naked-objects/etc/naked-objects-integtests.urm.puml +++ b/naked-objects/etc/naked-objects-integtests.urm.puml @@ -1,26 +1,18 @@ @startuml package domainapp.dom.app.homepage { - class HomePageService { - ~ container : DomainObjectContainer - + HomePageService() - + homePage() : HomePageViewModel - } class HomePageViewModel { ~ simpleObjects : SimpleObjects + HomePageViewModel() + getObjects() : List + title() : String } -} -package domainapp.dom.modules.simple { - class SimpleObjects { + class HomePageService { ~ container : DomainObjectContainer - + SimpleObjects() - + create(name : String) : SimpleObject - + findByName(name : String) : List - + listAll() : List - + title() : TranslatableString + + HomePageService() + + homePage() : HomePageViewModel } +} +package domainapp.dom.modules.simple { class SimpleObject { - container : DomainObjectContainer - dnFieldFlags : byte[] {static} @@ -79,6 +71,14 @@ package domainapp.dom.modules.simple { + updateName(name : String) : SimpleObject + validateUpdateName(name : String) : TranslatableString } + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } } package domainapp.fixture { class DomainAppFixturesProvider { diff --git a/null-object/etc/null-object.urm.puml b/null-object/etc/null-object.urm.puml index d0b2936c59ba..bd4f8446ea5c 100644 --- a/null-object/etc/null-object.urm.puml +++ b/null-object/etc/null-object.urm.puml @@ -1,9 +1,14 @@ @startuml package com.iluwatar.nullobject { - class NullNode { - - instance : NullNode {static} - - NullNode() - + getInstance() : NullNode {static} + class App { + + App() + + main(args : String[]) {static} + } + class NodeImpl { + - left : Node + - name : String + - right : Node + + NodeImpl(name : String, left : Node, right : Node) + getLeft() : Node + getName() : String + getRight() : Node @@ -17,15 +22,10 @@ package com.iluwatar.nullobject { + getTreeSize() : int {abstract} + walk() {abstract} } - class App { - + App() - + main(args : String[]) {static} - } - class NodeImpl { - - left : Node - - name : String - - right : Node - + NodeImpl(name : String, left : Node, right : Node) + class NullNode { + - instance : NullNode {static} + - NullNode() + + getInstance() : NullNode {static} + getLeft() : Node + getName() : String + getRight() : Node @@ -35,6 +35,6 @@ package com.iluwatar.nullobject { } NullNode --> "-instance" NullNode NodeImpl --> "-left" Node -NullNode ..|> Node NodeImpl ..|> Node +NullNode ..|> Node @enduml \ No newline at end of file diff --git a/object-pool/etc/object-pool.urm.puml b/object-pool/etc/object-pool.urm.puml index 9df1081d3355..caf0b6d7e90f 100644 --- a/object-pool/etc/object-pool.urm.puml +++ b/object-pool/etc/object-pool.urm.puml @@ -1,5 +1,9 @@ @startuml package com.iluwatar.object.pool { + class OliphauntPool { + + OliphauntPool() + # create() : Oliphaunt + } class Oliphaunt { - counter : int {static} - id : int @@ -7,14 +11,6 @@ package com.iluwatar.object.pool { + getId() : int + toString() : String } - class OliphauntPool { - + OliphauntPool() - # create() : Oliphaunt - } - class App { - + App() - + main(args : String[]) {static} - } abstract class ObjectPool { - available : HashSet - inUse : HashSet @@ -24,6 +20,10 @@ package com.iluwatar.object.pool { # create() : T {abstract} + toString() : String } + class App { + + App() + + main(args : String[]) {static} + } } OliphauntPool --|> ObjectPool @enduml \ No newline at end of file diff --git a/observer/etc/observer.urm.puml b/observer/etc/observer.urm.puml index 33485d7319ee..e9a8f7b9b18a 100644 --- a/observer/etc/observer.urm.puml +++ b/observer/etc/observer.urm.puml @@ -4,9 +4,12 @@ package com.iluwatar.observer { + Orcs() + update(currentWeather : WeatherType) } - class Hobbits { - + Hobbits() - + update(currentWeather : WeatherType) + interface WeatherObserver { + + update(WeatherType) {abstract} + } + class App { + + App() + + main(args : String[]) {static} } class Weather { - currentWeather : WeatherType @@ -17,12 +20,9 @@ package com.iluwatar.observer { + removeObserver(obs : WeatherObserver) + timePasses() } - class App { - + App() - + main(args : String[]) {static} - } - interface WeatherObserver { - + update(WeatherType) {abstract} + class Hobbits { + + Hobbits() + + update(currentWeather : WeatherType) } enum WeatherType { + COLD {static} @@ -41,6 +41,11 @@ package com.iluwatar.observer.generic { } interface Race { } + class GWeather { + - currentWeather : WeatherType + + GWeather() + + timePasses() + } abstract class Observable, A> { # observers : List> + Observable, A>() @@ -48,26 +53,21 @@ package com.iluwatar.observer.generic { + notifyObservers(argument : A) + removeObserver(observer : O extends Observer) } - class GWeather { - - currentWeather : WeatherType - + GWeather() - + timePasses() - } - interface Observer, O extends Observer, A> { - + update(S extends Observable, A) {abstract} - } class GHobbits { + GHobbits() + update(weather : GWeather, weatherType : WeatherType) } + interface Observer, O extends Observer, A> { + + update(S extends Observable, A) {abstract} + } } Weather --> "-currentWeather" WeatherType GWeather --> "-currentWeather" WeatherType Weather --> "-observers" WeatherObserver GOrcs ..|> Race Orcs ..|> WeatherObserver -Hobbits ..|> WeatherObserver Race --|> Observer GWeather --|> Observable GHobbits ..|> Race +Hobbits ..|> WeatherObserver @enduml \ No newline at end of file diff --git a/poison-pill/etc/poison-pill.urm.puml b/poison-pill/etc/poison-pill.urm.puml index 58f9eb93794a..f05c40692226 100644 --- a/poison-pill/etc/poison-pill.urm.puml +++ b/poison-pill/etc/poison-pill.urm.puml @@ -1,5 +1,8 @@ @startuml package com.iluwatar.poison.pill { + interface MqPublishPoint { + + put(Message) {abstract} + } interface Message { + POISON_PILL : Message {static} + addHeader(Headers, String) {abstract} @@ -12,21 +15,7 @@ package com.iluwatar.poison.pill { + App() + main(args : String[]) {static} } - class SimpleMessage { - - body : String - - headers : Map - + SimpleMessage() - + addHeader(header : Headers, value : String) - + getBody() : String - + getHeader(header : Headers) : String - + getHeaders() : Map - + setBody(body : String) - } - class SimpleMessageQueue { - - queue : BlockingQueue - + SimpleMessageQueue(bound : int) - + put(msg : Message) - + take() : Message + interface MessageQueue { } class Producer { - isStopped : boolean @@ -36,19 +25,30 @@ package com.iluwatar.poison.pill { + send(body : String) + stop() } - interface MqSubscribePoint { - + take() : Message {abstract} - } class Consumer { - name : String - queue : MqSubscribePoint + Consumer(name : String, queue : MqSubscribePoint) + consume() } - interface MessageQueue { + class SimpleMessageQueue { + - queue : BlockingQueue + + SimpleMessageQueue(bound : int) + + put(msg : Message) + + take() : Message } - interface MqPublishPoint { - + put(Message) {abstract} + interface MqSubscribePoint { + + take() : Message {abstract} + } + class SimpleMessage { + - body : String + - headers : Map + + SimpleMessage() + + addHeader(header : Headers, value : String) + + getBody() : String + + getHeader(header : Headers) : String + + getHeaders() : Map + + setBody(body : String) } enum Headers { + DATE {static} @@ -65,8 +65,8 @@ SimpleMessage --+ Message Producer --+ Message Message --> "-POISON_PILL" Message Consumer --+ Message -SimpleMessage ..|> Message -SimpleMessageQueue ..|> MessageQueue MessageQueue --|> MqPublishPoint MessageQueue --|> MqSubscribePoint +SimpleMessageQueue ..|> MessageQueue +SimpleMessage ..|> Message @enduml \ No newline at end of file diff --git a/pom.xml b/pom.xml index b1ae7d811785..fcf1671c8b97 100644 --- a/pom.xml +++ b/pom.xml @@ -282,6 +282,25 @@ + + + + com.github.markusmo3.urm + + + urm-maven-plugin + + + [1.4.1,) + + + map + + + + + + diff --git a/private-class-data/etc/private-class-data.urm.puml b/private-class-data/etc/private-class-data.urm.puml index 0edc2c1a835c..ad35c0bddeb9 100644 --- a/private-class-data/etc/private-class-data.urm.puml +++ b/private-class-data/etc/private-class-data.urm.puml @@ -1,28 +1,28 @@ @startuml package com.iluwatar.privateclassdata { - class Stew { + class StewData { - numCarrots : int - numMeat : int - numPeppers : int - numPotatoes : int - + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) - + mix() - + taste() + + StewData(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + getNumCarrots() : int + + getNumMeat() : int + + getNumPeppers() : int + + getNumPotatoes() : int } class App { + App() + main(args : String[]) {static} } - class StewData { + class Stew { - numCarrots : int - numMeat : int - numPeppers : int - numPotatoes : int - + StewData(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) - + getNumCarrots() : int - + getNumMeat() : int - + getNumPeppers() : int - + getNumPotatoes() : int + + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + + taste() } class ImmutableStew { - data : StewData diff --git a/producer-consumer/etc/producer-consumer.urm.puml b/producer-consumer/etc/producer-consumer.urm.puml index 9a67485523df..8b793d75b847 100644 --- a/producer-consumer/etc/producer-consumer.urm.puml +++ b/producer-consumer/etc/producer-consumer.urm.puml @@ -1,17 +1,10 @@ @startuml package com.iluwatar.producer.consumer { - class Producer { - - itemId : int + class Consumer { - name : String - queue : ItemQueue - + Producer(name : String, queue : ItemQueue) - + produce() - } - class ItemQueue { - - queue : BlockingQueue - + ItemQueue() - + put(item : Item) - + take() : Item + + Consumer(name : String, queue : ItemQueue) + + consume() } class Item { - id : int @@ -24,11 +17,18 @@ package com.iluwatar.producer.consumer { + App() + main(args : String[]) {static} } - class Consumer { + class Producer { + - itemId : int - name : String - queue : ItemQueue - + Consumer(name : String, queue : ItemQueue) - + consume() + + Producer(name : String, queue : ItemQueue) + + produce() + } + class ItemQueue { + - queue : BlockingQueue + + ItemQueue() + + put(item : Item) + + take() : Item } } Consumer --> "-queue" ItemQueue diff --git a/promise/etc/promise.urm.puml b/promise/etc/promise.urm.puml index d97871411aed..cd6e732529fc 100644 --- a/promise/etc/promise.urm.puml +++ b/promise/etc/promise.urm.puml @@ -24,12 +24,18 @@ package com.iluwatar.promise { - ConsumeAction(src : Promise, dest : Promise, action : Consumer) + run() } - -class TransformAction { - - dest : Promise - - func : Function - - src : Promise - - TransformAction(src : Promise, dest : Promise, func : Function) - + run() + class Promise { + - exceptionHandler : Consumer + - fulfillmentAction : Runnable + + Promise() + + fulfill(value : T) + + fulfillExceptionally(exception : Exception) + + fulfillInAsync(task : Callable, executor : Executor) : Promise + - handleException(exception : Exception) + + onError(exceptionHandler : Consumer) : Promise + - postFulfillment() + + thenAccept(action : Consumer) : Promise + + thenApply(func : Function) : Promise } class App { - DEFAULT_URL : String {static} @@ -47,18 +53,12 @@ package com.iluwatar.promise { - stop() - taskCompleted() } - class Promise { - - exceptionHandler : Consumer - - fulfillmentAction : Runnable - + Promise() - + fulfill(value : T) - + fulfillExceptionally(exception : Exception) - + fulfillInAsync(task : Callable, executor : Executor) : Promise - - handleException(exception : Exception) - + onError(exceptionHandler : Consumer) : Promise - - postFulfillment() - + thenAccept(action : Consumer) : Promise - + thenApply(func : Function) : Promise + -class TransformAction { + - dest : Promise + - func : Function + - src : Promise + - TransformAction(src : Promise, dest : Promise, func : Function) + + run() } class Utility { + Utility() diff --git a/property/etc/property.urm.puml b/property/etc/property.urm.puml index 7c90edccc847..6ff0e5c52ed6 100644 --- a/property/etc/property.urm.puml +++ b/property/etc/property.urm.puml @@ -1,5 +1,15 @@ @startuml package com.iluwatar.property { + class App { + + App() + + main(args : String[]) {static} + } + interface Prototype { + + get(Stats) : Integer {abstract} + + has(Stats) : boolean {abstract} + + remove(Stats) {abstract} + + set(Stats, Integer) {abstract} + } class Character { - name : String - properties : Map @@ -16,15 +26,12 @@ package com.iluwatar.property { + toString() : String + type() : Type } - interface Prototype { - + get(Stats) : Integer {abstract} - + has(Stats) : boolean {abstract} - + remove(Stats) {abstract} - + set(Stats, Integer) {abstract} - } - class App { - + App() - + main(args : String[]) {static} + enum Type { + + MAGE {static} + + ROGUE {static} + + WARRIOR {static} + + valueOf(name : String) : Type {static} + + values() : Type[] {static} } enum Stats { + AGILITY {static} @@ -38,13 +45,6 @@ package com.iluwatar.property { + valueOf(name : String) : Stats {static} + values() : Stats[] {static} } - enum Type { - + MAGE {static} - + ROGUE {static} - + WARRIOR {static} - + valueOf(name : String) : Type {static} - + values() : Type[] {static} - } } App --+ Character Character --> "-prototype" Prototype diff --git a/prototype/etc/prototype.urm.puml b/prototype/etc/prototype.urm.puml index 7bfc00e15625..7ab26dd1b3e4 100644 --- a/prototype/etc/prototype.urm.puml +++ b/prototype/etc/prototype.urm.puml @@ -1,59 +1,42 @@ @startuml package com.iluwatar.prototype { - interface HeroFactory { - + createBeast() : Beast {abstract} - + createMage() : Mage {abstract} - + createWarlord() : Warlord {abstract} + class OrcWarlord { + + OrcWarlord() + + clone() : Warlord + + toString() : String } class OrcBeast { + OrcBeast() + clone() : Beast + toString() : String } - abstract class Mage { - + Mage() - + clone() : Mage {abstract} - } - class HeroFactoryImpl { - - beast : Beast - - mage : Mage - - warlord : Warlord - + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) - + createBeast() : Beast - + createMage() : Mage - + createWarlord() : Warlord + abstract class Beast { + + Beast() + + clone() : Beast {abstract} } class ElfMage { + ElfMage() + clone() : Mage + toString() : String } + abstract class Mage { + + Mage() + + clone() : Mage {abstract} + } abstract class Prototype { + Prototype() + clone() : Object {abstract} } - class App { - + App() - + main(args : String[]) {static} - } - abstract class Warlord { - + Warlord() - + clone() : Warlord {abstract} - } - class OrcWarlord { - + OrcWarlord() - + clone() : Warlord - + toString() : String + interface HeroFactory { + + createBeast() : Beast {abstract} + + createMage() : Mage {abstract} + + createWarlord() : Warlord {abstract} } class ElfWarlord { + ElfWarlord() + clone() : Warlord + toString() : String } - abstract class Beast { - + Beast() - + clone() : Beast {abstract} - } class OrcMage { + OrcMage() + clone() : Mage @@ -64,18 +47,35 @@ package com.iluwatar.prototype { + clone() : Beast + toString() : String } + abstract class Warlord { + + Warlord() + + clone() : Warlord {abstract} + } + class HeroFactoryImpl { + - beast : Beast + - mage : Mage + - warlord : Warlord + + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) + + createBeast() : Beast + + createMage() : Mage + + createWarlord() : Warlord + } + class App { + + App() + + main(args : String[]) {static} + } } HeroFactoryImpl --> "-beast" Beast HeroFactoryImpl --> "-warlord" Warlord HeroFactoryImpl --> "-mage" Mage +OrcWarlord --|> Warlord OrcBeast --|> Beast -Mage --|> Prototype -HeroFactoryImpl ..|> HeroFactory +Beast --|> Prototype ElfMage --|> Mage -Warlord --|> Prototype -OrcWarlord --|> Warlord +Mage --|> Prototype ElfWarlord --|> Warlord -Beast --|> Prototype OrcMage --|> Mage ElfBeast --|> Beast +Warlord --|> Prototype +HeroFactoryImpl ..|> HeroFactory @enduml \ No newline at end of file diff --git a/proxy/etc/proxy.urm.puml b/proxy/etc/proxy.urm.puml index 4203174de6e6..1dc58087c7c3 100644 --- a/proxy/etc/proxy.urm.puml +++ b/proxy/etc/proxy.urm.puml @@ -1,5 +1,11 @@ @startuml package com.iluwatar.proxy { + class WizardTowerProxy { + - NUM_WIZARDS_ALLOWED : int {static} + - numWizards : int + + WizardTowerProxy() + + enter(wizard : Wizard) + } class WizardTower { + WizardTower() + enter(wizard : Wizard) @@ -8,12 +14,6 @@ package com.iluwatar.proxy { + App() + main(args : String[]) {static} } - class WizardTowerProxy { - - NUM_WIZARDS_ALLOWED : int {static} - - numWizards : int - + WizardTowerProxy() - + enter(wizard : Wizard) - } class Wizard { - name : String + Wizard(name : String) diff --git a/reactor/etc/reactor.urm.puml b/reactor/etc/reactor.urm.puml index 302f2663c3a2..ecf2f40f72f4 100644 --- a/reactor/etc/reactor.urm.puml +++ b/reactor/etc/reactor.urm.puml @@ -1,5 +1,24 @@ @startuml package com.iluwatar.reactor.app { + class App { + - channels : List + - dispatcher : Dispatcher + - reactor : NioReactor + + App(dispatcher : Dispatcher) + + main(args : String[]) {static} + + start() + + stop() + - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + } + class LoggingHandler { + - ACK : byte[] {static} + + LoggingHandler() + - doLogging(data : ByteBuffer) {static} + + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} + - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} + } ~class TcpLoggingClient { - clientName : String - serverPort : int @@ -13,14 +32,6 @@ package com.iluwatar.reactor.app { + UdpLoggingClient(clientName : String, port : int) + run() } - class LoggingHandler { - - ACK : byte[] {static} - + LoggingHandler() - - doLogging(data : ByteBuffer) {static} - + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) - - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} - - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} - } class AppClient { - service : ExecutorService + AppClient() @@ -29,33 +40,30 @@ package com.iluwatar.reactor.app { + start() + stop() } - class App { - - channels : List - - dispatcher : Dispatcher - - reactor : NioReactor - + App(dispatcher : Dispatcher) - + main(args : String[]) {static} - + start() - + stop() - - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel - - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel - } } package com.iluwatar.reactor.framework { - interface Dispatcher { - + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} - + stop() {abstract} - } - class SameThreadDispatcher { - + SameThreadDispatcher() - + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) - + stop() + abstract class AbstractNioChannel { + - channel : SelectableChannel + - channelToPendingWrites : Map> + - handler : ChannelHandler + - reactor : NioReactor + + AbstractNioChannel(handler : ChannelHandler, channel : SelectableChannel) + + bind() {abstract} + # doWrite(Object, SelectionKey) {abstract} + ~ flush(key : SelectionKey) + + getHandler() : ChannelHandler + + getInterestedOps() : int {abstract} + + getJavaChannel() : SelectableChannel + + read(SelectionKey) : Object {abstract} + ~ setReactor(reactor : NioReactor) + + write(data : Object, key : SelectionKey) } - class ThreadPoolDispatcher { - - executorService : ExecutorService - + ThreadPoolDispatcher(poolSize : int) - + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) - + stop() + ~class ChangeKeyOpsCommand { + - interestedOps : int + - key : SelectionKey + + ChangeKeyOpsCommand(this$0 : SelectionKey, key : int) + + run() + + toString() : String } interface ChannelHandler { + handleChannelRead(AbstractNioChannel, Object, SelectionKey) {abstract} @@ -70,6 +78,30 @@ package com.iluwatar.reactor.framework { + read(key : SelectionKey) : DatagramPacket + write(data : Object, key : SelectionKey) } + class ThreadPoolDispatcher { + - executorService : ExecutorService + + ThreadPoolDispatcher(poolSize : int) + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + class NioServerSocketChannel { + - port : int + + NioServerSocketChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : ServerSocketChannel + + read(key : SelectionKey) : ByteBuffer + } + class SameThreadDispatcher { + + SameThreadDispatcher() + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + interface Dispatcher { + + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} + + stop() {abstract} + } class DatagramPacket { - data : ByteBuffer - receiver : SocketAddress @@ -81,31 +113,6 @@ package com.iluwatar.reactor.framework { + setReceiver(receiver : SocketAddress) + setSender(sender : SocketAddress) } - abstract class AbstractNioChannel { - - channel : SelectableChannel - - channelToPendingWrites : Map> - - handler : ChannelHandler - - reactor : NioReactor - + AbstractNioChannel(handler : ChannelHandler, channel : SelectableChannel) - + bind() {abstract} - # doWrite(Object, SelectionKey) {abstract} - ~ flush(key : SelectionKey) - + getHandler() : ChannelHandler - + getInterestedOps() : int {abstract} - + getJavaChannel() : SelectableChannel - + read(SelectionKey) : Object {abstract} - ~ setReactor(reactor : NioReactor) - + write(data : Object, key : SelectionKey) - } - class NioServerSocketChannel { - - port : int - + NioServerSocketChannel(port : int, handler : ChannelHandler) - + bind() - # doWrite(pendingWrite : Object, key : SelectionKey) - + getInterestedOps() : int - + getJavaChannel() : ServerSocketChannel - + read(key : SelectionKey) : ByteBuffer - } class NioReactor { - dispatcher : Dispatcher - pendingCommands : Queue @@ -124,28 +131,21 @@ package com.iluwatar.reactor.framework { + start() + stop() } - ~class ChangeKeyOpsCommand { - - interestedOps : int - - key : SelectionKey - + ChangeKeyOpsCommand(this$0 : NioReactor, key : SelectionKey, interestedOps : int) - + run() - + toString() : String - } } AbstractNioChannel --> "-handler" ChannelHandler UdpLoggingClient ..+ AppClient -AbstractNioChannel --> "-reactor" NioReactor TcpLoggingClient ..+ AppClient +AbstractNioChannel --> "-reactor" NioReactor NioReactor --> "-dispatcher" Dispatcher App --> "-reactor" NioReactor App --> "-channels" AbstractNioChannel DatagramPacket ..+ NioDatagramChannel -ChangeKeyOpsCommand --+ NioReactor -App --> "-dispatcher" Dispatcher LoggingHandler --+ NioDatagramChannel -SameThreadDispatcher ..|> Dispatcher +App --> "-dispatcher" Dispatcher +ChangeKeyOpsCommand --+ NioReactor +NioDatagramChannel --|> AbstractNioChannel LoggingHandler ..|> ChannelHandler ThreadPoolDispatcher ..|> Dispatcher -NioDatagramChannel --|> AbstractNioChannel NioServerSocketChannel --|> AbstractNioChannel +SameThreadDispatcher ..|> Dispatcher @enduml \ No newline at end of file diff --git a/reader-writer-lock/etc/reader-writer-lock.urm.puml b/reader-writer-lock/etc/reader-writer-lock.urm.puml index 22b303cf50a2..1760cf3e46d0 100644 --- a/reader-writer-lock/etc/reader-writer-lock.urm.puml +++ b/reader-writer-lock/etc/reader-writer-lock.urm.puml @@ -1,21 +1,5 @@ @startuml package com.iluwatar.reader.writer.lock { - -class ReadLock { - - ReadLock(ReaderWriterLock) - + lock() - + lockInterruptibly() - + newCondition() : Condition - + tryLock() : boolean - + tryLock(time : long, unit : TimeUnit) : boolean - + unlock() - } - class Writer { - - name : String - - writeLock : Lock - + Writer(name : String, writeLock : Lock) - + run() - + write() - } class ReaderWriterLock { - currentReaderCount : int - globalMutex : Set @@ -31,7 +15,7 @@ package com.iluwatar.reader.writer.lock { + writeLock() : Lock } -class WriteLock { - - WriteLock(ReaderWriterLock) + - WriteLock() + lock() + lockInterruptibly() + newCondition() : Condition @@ -43,6 +27,22 @@ package com.iluwatar.reader.writer.lock { + App() + main(args : String[]) {static} } + -class ReadLock { + - ReadLock() + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class Writer { + - name : String + - writeLock : Lock + + Writer(name : String, writeLock : Lock) + + run() + + write() + } class Reader { - name : String - readLock : Lock @@ -51,8 +51,8 @@ package com.iluwatar.reader.writer.lock { + run() } } -ReadLock --+ ReaderWriterLock ReaderWriterLock --> "-readerLock" ReadLock +ReadLock --+ ReaderWriterLock ReaderWriterLock --> "-writerLock" WriteLock WriteLock --+ ReaderWriterLock @enduml \ No newline at end of file diff --git a/repository/etc/repository.urm.puml b/repository/etc/repository.urm.puml index 49a2c8fdc72b..ad1759033bae 100644 --- a/repository/etc/repository.urm.puml +++ b/repository/etc/repository.urm.puml @@ -1,9 +1,34 @@ @startuml package com.iluwatar.repository { + class PersonSpecifications { + + PersonSpecifications() + } + class NameEqualSpec { + + name : String + + NameEqualSpec(name : String) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } class App { + App() + main(args : String[]) {static} } + interface PersonRepository { + + findByName(String) : Person {abstract} + } + class AgeBetweenSpec { + - from : int + - to : int + + AgeBetweenSpec(from : int, to : int) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class AppConfig { + + AppConfig() + + dataSource() : DataSource + + entityManagerFactory() : LocalContainerEntityManagerFactoryBean + - jpaProperties() : Properties {static} + + main(args : String[]) {static} + + transactionManager() : JpaTransactionManager + } class Person { - age : int - id : Long @@ -23,31 +48,6 @@ package com.iluwatar.repository { + setSurname(surname : String) + toString() : String } - class AgeBetweenSpec { - - from : int - - to : int - + AgeBetweenSpec(from : int, to : int) - + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate - } - class AppConfig { - + AppConfig() - + dataSource() : DataSource - + entityManagerFactory() : LocalContainerEntityManagerFactoryBean - - jpaProperties() : Properties {static} - + main(args : String[]) {static} - + transactionManager() : JpaTransactionManager - } - interface PersonRepository { - + findByName(String) : Person {abstract} - } - class NameEqualSpec { - + name : String - + NameEqualSpec(name : String) - + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate - } - class PersonSpecifications { - + PersonSpecifications() - } } App --+ PersonSpecifications AppConfig --+ PersonSpecifications diff --git a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml index 847b716a02db..81560c7f5289 100644 --- a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml +++ b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml @@ -4,13 +4,13 @@ package com.iluwatar.resource.acquisition.is.initialization { + App() + main(args : String[]) {static} } - class TreasureChest { - + TreasureChest() - + close() - } class SlidingDoor { + SlidingDoor() + close() } + class TreasureChest { + + TreasureChest() + + close() + } } @enduml \ No newline at end of file diff --git a/semaphore/etc/semaphore.urm.puml b/semaphore/etc/semaphore.urm.puml index f85fff92175d..a94ff5a7cace 100644 --- a/semaphore/etc/semaphore.urm.puml +++ b/semaphore/etc/semaphore.urm.puml @@ -1,32 +1,11 @@ @startuml package com.iluwatar.semaphore { - class FruitShop { - - available : boolean[] - - bowls : FruitBowl[] - - semaphore : Semaphore - + FruitShop() - + countFruit() : int - + returnBowl(bowl : FruitBowl) - + takeBowl() : FruitBowl - } - class FruitBowl { - - fruit : ArrayList - + FruitBowl() - + countFruit() : int - + put(f : Fruit) - + take() : Fruit - + toString() : String - } class Fruit { - type : FruitType + Fruit(type : FruitType) + getType() : FruitType + toString() : String } - interface Lock { - + acquire() {abstract} - + release() {abstract} - } class App { + App() + main(args : String[]) {static} @@ -40,6 +19,27 @@ package com.iluwatar.semaphore { + getNumLicenses() : int + release() } + class FruitShop { + - available : boolean[] + - bowls : FruitBowl[] + - semaphore : Semaphore + + FruitShop() + + countFruit() : int + + returnBowl(bowl : FruitBowl) + + takeBowl() : FruitBowl + } + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class FruitBowl { + - fruit : ArrayList + + FruitBowl() + + countFruit() : int + + put(f : Fruit) + + take() : Fruit + + toString() : String + } enum FruitType { + APPLE {static} + LEMON {static} diff --git a/servant/etc/servant.urm.puml b/servant/etc/servant.urm.puml index 48d48bd842d0..b7391f5dd21d 100644 --- a/servant/etc/servant.urm.puml +++ b/servant/etc/servant.urm.puml @@ -1,24 +1,5 @@ @startuml package com.iluwatar.servant { - class King { - - complimentReceived : boolean - - isDrunk : boolean - - isHappy : boolean - - isHungry : boolean - + King() - + changeMood() - + getDrink() - + getFed() - + getMood() : boolean - + receiveCompliments() - } - ~interface Royalty { - + changeMood() {abstract} - + getDrink() {abstract} - + getFed() {abstract} - + getMood() : boolean {abstract} - + receiveCompliments() {abstract} - } class Servant { + name : String + Servant(name : String) @@ -48,8 +29,27 @@ package com.iluwatar.servant { + main(args : String[]) {static} + scenario(servant : Servant, compliment : int) {static} } + class King { + - complimentReceived : boolean + - isDrunk : boolean + - isHappy : boolean + - isHungry : boolean + + King() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + } + ~interface Royalty { + + changeMood() {abstract} + + getDrink() {abstract} + + getFed() {abstract} + + getMood() : boolean {abstract} + + receiveCompliments() {abstract} + } } App --> "-jenkins" Servant -King ..|> Royalty Queen ..|> Royalty +King ..|> Royalty @enduml \ No newline at end of file diff --git a/service-layer/etc/service-layer.urm.puml b/service-layer/etc/service-layer.urm.puml index c67ffa645560..880384b9f357 100644 --- a/service-layer/etc/service-layer.urm.puml +++ b/service-layer/etc/service-layer.urm.puml @@ -55,6 +55,9 @@ package com.iluwatar.servicelayer.magic { } } package com.iluwatar.servicelayer.wizard { + interface WizardDao { + + findByName(String) : Wizard {abstract} + } class Wizard { - id : Long - name : String @@ -74,9 +77,6 @@ package com.iluwatar.servicelayer.wizard { + WizardDaoImpl() + findByName(name : String) : Wizard } - interface WizardDao { - + findByName(String) : Wizard {abstract} - } } package com.iluwatar.servicelayer.app { class App { @@ -86,33 +86,14 @@ package com.iluwatar.servicelayer.app { + queryData() {static} } } -package com.iluwatar.servicelayer.spell { - class SpellDaoImpl { - + SpellDaoImpl() - + findByName(name : String) : Spell - } - class Spell { - - id : Long - - name : String - - spellbook : Spellbook - + Spell() - + Spell(name : String) - + getId() : Long - + getName() : String - + getSpellbook() : Spellbook - + setId(id : Long) - + setName(name : String) - + setSpellbook(spellbook : Spellbook) - + toString() : String - } - interface SpellDao { - + findByName(String) : Spell {abstract} - } -} package com.iluwatar.servicelayer.spellbook { interface SpellbookDao { + findByName(String) : Spellbook {abstract} } + class SpellbookDaoImpl { + + SpellbookDaoImpl() + + findByName(name : String) : Spellbook + } class Spellbook { - id : Long - name : String @@ -131,9 +112,28 @@ package com.iluwatar.servicelayer.spellbook { + setWizards(wizards : Set) + toString() : String } - class SpellbookDaoImpl { - + SpellbookDaoImpl() - + findByName(name : String) : Spellbook +} +package com.iluwatar.servicelayer.spell { + class SpellDaoImpl { + + SpellDaoImpl() + + findByName(name : String) : Spell + } + interface SpellDao { + + findByName(String) : Spell {abstract} + } + class Spell { + - id : Long + - name : String + - spellbook : Spellbook + + Spell() + + Spell(name : String) + + getId() : Long + + getName() : String + + getSpellbook() : Spellbook + + setId(id : Long) + + setName(name : String) + + setSpellbook(spellbook : Spellbook) + + toString() : String } } MagicServiceImpl --> "-wizardDao" WizardDao @@ -141,18 +141,18 @@ MagicServiceImpl --> "-spellbookDao" SpellbookDao MagicServiceImpl --> "-spellDao" SpellDao Spellbook --> "-spells" Spell Spellbook --> "-wizards" Wizard -Wizard --|> BaseEntity -SpellbookDao --|> Dao SpellDaoImpl ..|> SpellDao SpellDaoImpl --|> DaoBaseImpl -MagicServiceImpl ..|> MagicService -DaoBaseImpl ..|> Dao -WizardDaoImpl ..|> WizardDao -WizardDaoImpl --|> DaoBaseImpl -Spellbook --|> BaseEntity +SpellbookDao --|> Dao +WizardDao --|> Dao SpellbookDaoImpl ..|> SpellbookDao SpellbookDaoImpl --|> DaoBaseImpl -Spell --|> BaseEntity -WizardDao --|> Dao +MagicServiceImpl ..|> MagicService SpellDao --|> Dao +Spell --|> BaseEntity +Spellbook --|> BaseEntity +Wizard --|> BaseEntity +WizardDaoImpl ..|> WizardDao +WizardDaoImpl --|> DaoBaseImpl +DaoBaseImpl ..|> Dao @enduml \ No newline at end of file diff --git a/service-locator/etc/service-locator.urm.puml b/service-locator/etc/service-locator.urm.puml index 085b05b285e3..9e41e7f40555 100644 --- a/service-locator/etc/service-locator.urm.puml +++ b/service-locator/etc/service-locator.urm.puml @@ -1,25 +1,10 @@ @startuml package com.iluwatar.servicelocator { - interface Service { - + execute() {abstract} - + getId() : int {abstract} - + getName() : String {abstract} - } - class InitContext { - + InitContext() - + lookup(serviceName : String) : Object - } class ServiceLocator { - serviceCache : ServiceCache {static} - ServiceLocator() + getService(serviceJndiName : String) : Service {static} } - class ServiceCache { - - serviceCache : Map - + ServiceCache() - + addService(newService : Service) - + getService(serviceName : String) : Service - } class App { + App() + main(args : String[]) {static} @@ -32,6 +17,21 @@ package com.iluwatar.servicelocator { + getId() : int + getName() : String } + class InitContext { + + InitContext() + + lookup(serviceName : String) : Object + } + class ServiceCache { + - serviceCache : Map + + ServiceCache() + + addService(newService : Service) + + getService(serviceName : String) : Service + } + interface Service { + + execute() {abstract} + + getId() : int {abstract} + + getName() : String {abstract} + } } ServiceLocator --> "-serviceCache" ServiceCache ServiceImpl ..|> Service diff --git a/singleton/etc/singleton.urm.puml b/singleton/etc/singleton.urm.puml index f5ec198794a0..a130917729d2 100644 --- a/singleton/etc/singleton.urm.puml +++ b/singleton/etc/singleton.urm.puml @@ -1,32 +1,32 @@ @startuml package com.iluwatar.singleton { + class App { + + App() + + main(args : String[]) {static} + } class ThreadSafeLazyLoadedIvoryTower { - instance : ThreadSafeLazyLoadedIvoryTower {static} - ThreadSafeLazyLoadedIvoryTower() + getInstance() : ThreadSafeLazyLoadedIvoryTower {static} } - -class HelperHolder { - + INSTANCE : InitializingOnDemandHolderIdiom {static} - - HelperHolder() - } - class App { - + App() - + main(args : String[]) {static} + class InitializingOnDemandHolderIdiom { + - InitializingOnDemandHolderIdiom() + + getInstance() : InitializingOnDemandHolderIdiom {static} } class ThreadSafeDoubleCheckLocking { - instance : ThreadSafeDoubleCheckLocking {static} - ThreadSafeDoubleCheckLocking() + getInstance() : ThreadSafeDoubleCheckLocking {static} } - class InitializingOnDemandHolderIdiom { - - InitializingOnDemandHolderIdiom() - + getInstance() : InitializingOnDemandHolderIdiom {static} - } class IvoryTower { - INSTANCE : IvoryTower {static} - IvoryTower() + getInstance() : IvoryTower {static} } + -class HelperHolder { + - INSTANCE : InitializingOnDemandHolderIdiom {static} + - HelperHolder() + } enum EnumIvoryTower { + INSTANCE {static} + toString() : String diff --git a/specification/etc/specification.urm.puml b/specification/etc/specification.urm.puml index 0009a1bcd18e..2f194d340653 100644 --- a/specification/etc/specification.urm.puml +++ b/specification/etc/specification.urm.puml @@ -1,17 +1,5 @@ @startuml package com.iluwatar.specification.creature { - class Goblin { - + Goblin() - } - interface Creature { - + getColor() : Color {abstract} - + getMovement() : Movement {abstract} - + getName() : String {abstract} - + getSize() : Size {abstract} - } - class Troll { - + Troll() - } abstract class AbstractCreature { - color : Color - movement : Movement @@ -24,20 +12,41 @@ package com.iluwatar.specification.creature { + getSize() : Size + toString() : String } + class Troll { + + Troll() + } + class Octopus { + + Octopus() + } class Shark { + Shark() } + class Goblin { + + Goblin() + } class KillerBee { + KillerBee() } - class Octopus { - + Octopus() + interface Creature { + + getColor() : Color {abstract} + + getMovement() : Movement {abstract} + + getName() : String {abstract} + + getSize() : Size {abstract} } class Dragon { + Dragon() } } package com.iluwatar.specification.property { + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } enum Color { + DARK {static} + GREEN {static} @@ -57,15 +66,6 @@ package com.iluwatar.specification.property { + valueOf(name : String) : Movement {static} + values() : Movement[] {static} } - enum Size { - + LARGE {static} - + NORMAL {static} - + SMALL {static} - - title : String - + toString() : String - + valueOf(name : String) : Size {static} - + values() : Size[] {static} - } } package com.iluwatar.specification.app { class App { @@ -74,9 +74,9 @@ package com.iluwatar.specification.app { } } package com.iluwatar.specification.selector { - class SizeSelector { - - s : Size - + SizeSelector(s : Size) + class MovementSelector { + - m : Movement + + MovementSelector(m : Movement) + test(t : Creature) : boolean } class ColorSelector { @@ -84,9 +84,9 @@ package com.iluwatar.specification.selector { + ColorSelector(c : Color) + test(t : Creature) : boolean } - class MovementSelector { - - m : Movement - + MovementSelector(m : Movement) + class SizeSelector { + - s : Size + + SizeSelector(s : Size) + test(t : Creature) : boolean } } @@ -96,11 +96,11 @@ MovementSelector --> "-m" Movement AbstractCreature --> "-movement" Movement AbstractCreature --> "-size" Size ColorSelector --> "-c" Color -Goblin --|> AbstractCreature -Troll --|> AbstractCreature AbstractCreature ..|> Creature +Troll --|> AbstractCreature +Octopus --|> AbstractCreature Shark --|> AbstractCreature +Goblin --|> AbstractCreature KillerBee --|> AbstractCreature -Octopus --|> AbstractCreature Dragon --|> AbstractCreature @enduml \ No newline at end of file diff --git a/state/etc/state.urm.puml b/state/etc/state.urm.puml index a0951ff6e4ef..378c11cd1f5e 100644 --- a/state/etc/state.urm.puml +++ b/state/etc/state.urm.puml @@ -1,5 +1,19 @@ @startuml package com.iluwatar.state { + class PeacefulState { + - mammoth : Mammoth + + PeacefulState(mammoth : Mammoth) + + observe() + + onEnterState() + } + interface State { + + observe() {abstract} + + onEnterState() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } class AngryState { - mammoth : Mammoth + AngryState(mammoth : Mammoth) @@ -14,24 +28,10 @@ package com.iluwatar.state { + timePasses() + toString() : String } - interface State { - + observe() {abstract} - + onEnterState() {abstract} - } - class PeacefulState { - - mammoth : Mammoth - + PeacefulState(mammoth : Mammoth) - + observe() - + onEnterState() - } - class App { - + App() - + main(args : String[]) {static} - } } -PeacefulState --> "-mammoth" Mammoth AngryState --> "-mammoth" Mammoth +PeacefulState --> "-mammoth" Mammoth Mammoth --> "-state" State -AngryState ..|> State PeacefulState ..|> State +AngryState ..|> State @enduml \ No newline at end of file diff --git a/step-builder/etc/step-builder.urm.puml b/step-builder/etc/step-builder.urm.puml index cc1f88ef00ad..129ecbf30cb1 100644 --- a/step-builder/etc/step-builder.urm.puml +++ b/step-builder/etc/step-builder.urm.puml @@ -1,8 +1,16 @@ @startuml package com.iluwatar.stepbuilder { + interface AbilityStep { + + noAbilities() : BuildStep {abstract} + + noMoreAbilities() : BuildStep {abstract} + + withAbility(String) : AbilityStep {abstract} + } interface BuildStep { + build() : Character {abstract} } + interface NameStep { + + name(String) : ClassStep {abstract} + } -class CharacterSteps { - abilities : List - fighterClass : String @@ -27,22 +35,6 @@ package com.iluwatar.stepbuilder { + App() + main(args : String[]) {static} } - interface ClassStep { - + fighterClass(String) : WeaponStep {abstract} - + wizardClass(String) : SpellStep {abstract} - } - interface WeaponStep { - + noWeapon() : BuildStep {abstract} - + withWeapon(String) : AbilityStep {abstract} - } - interface AbilityStep { - + noAbilities() : BuildStep {abstract} - + noMoreAbilities() : BuildStep {abstract} - + withAbility(String) : AbilityStep {abstract} - } - interface NameStep { - + name(String) : ClassStep {abstract} - } class CharacterStepBuilder { - CharacterStepBuilder() + newBuilder() : NameStep {static} @@ -73,13 +65,21 @@ package com.iluwatar.stepbuilder { + noSpell() : BuildStep {abstract} + withSpell(String) : AbilityStep {abstract} } + interface ClassStep { + + fighterClass(String) : WeaponStep {abstract} + + wizardClass(String) : SpellStep {abstract} + } + interface WeaponStep { + + noWeapon() : BuildStep {abstract} + + withWeapon(String) : AbilityStep {abstract} + } } App --+ CharacterStepBuilder WeaponStep ..+ CharacterStepBuilder -SpellStep ..+ CharacterStepBuilder -AbilityStep ..+ CharacterStepBuilder ClassStep ..+ CharacterStepBuilder +SpellStep ..+ CharacterStepBuilder CharacterSteps ..+ CharacterStepBuilder +AbilityStep ..+ CharacterStepBuilder NameStep ..+ CharacterStepBuilder BuildStep ..+ CharacterStepBuilder CharacterSteps ..|> NameStep diff --git a/strategy/etc/strategy.urm.puml b/strategy/etc/strategy.urm.puml index 2cc07286360b..db86b9ed6a0e 100644 --- a/strategy/etc/strategy.urm.puml +++ b/strategy/etc/strategy.urm.puml @@ -6,28 +6,28 @@ package com.iluwatar.strategy { + changeStrategy(strategy : DragonSlayingStrategy) + goToBattle() } - class SpellStrategy { - + SpellStrategy() - + execute() - } - class ProjectileStrategy { - + ProjectileStrategy() - + execute() - } interface DragonSlayingStrategy { + execute() {abstract} } + class App { + + App() + + main(args : String[]) {static} + } class MeleeStrategy { + MeleeStrategy() + execute() } - class App { - + App() - + main(args : String[]) {static} + class ProjectileStrategy { + + ProjectileStrategy() + + execute() + } + class SpellStrategy { + + SpellStrategy() + + execute() } } DragonSlayer --> "-strategy" DragonSlayingStrategy -SpellStrategy ..|> DragonSlayingStrategy -ProjectileStrategy ..|> DragonSlayingStrategy MeleeStrategy ..|> DragonSlayingStrategy +ProjectileStrategy ..|> DragonSlayingStrategy +SpellStrategy ..|> DragonSlayingStrategy @enduml \ No newline at end of file diff --git a/template-method/etc/template-method.urm.puml b/template-method/etc/template-method.urm.puml index c98287bc6ee8..c2c6045fe404 100644 --- a/template-method/etc/template-method.urm.puml +++ b/template-method/etc/template-method.urm.puml @@ -1,17 +1,17 @@ @startuml package com.iluwatar.templatemethod { - class SubtleMethod { - + SubtleMethod() - # confuseTarget(target : String) - # pickTarget() : String - # stealTheItem(target : String) - } class HitAndRunMethod { + HitAndRunMethod() # confuseTarget(target : String) # pickTarget() : String # stealTheItem(target : String) } + class HalflingThief { + - method : StealingMethod + + HalflingThief(method : StealingMethod) + + changeMethod(method : StealingMethod) + + steal() + } abstract class StealingMethod { + StealingMethod() # confuseTarget(String) {abstract} @@ -19,18 +19,18 @@ package com.iluwatar.templatemethod { + steal() # stealTheItem(String) {abstract} } + class SubtleMethod { + + SubtleMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } class App { + App() + main(args : String[]) {static} } - class HalflingThief { - - method : StealingMethod - + HalflingThief(method : StealingMethod) - + changeMethod(method : StealingMethod) - + steal() - } } HalflingThief --> "-method" StealingMethod -SubtleMethod --|> StealingMethod HitAndRunMethod --|> StealingMethod +SubtleMethod --|> StealingMethod @enduml \ No newline at end of file diff --git a/thread-pool/etc/thread-pool.urm.puml b/thread-pool/etc/thread-pool.urm.puml index 2b73e2d533fd..421ea1dcedf3 100644 --- a/thread-pool/etc/thread-pool.urm.puml +++ b/thread-pool/etc/thread-pool.urm.puml @@ -1,14 +1,5 @@ @startuml package com.iluwatar.threadpool { - class Worker { - - task : Task - + Worker(task : Task) - + run() - } - class App { - + App() - + main(args : String[]) {static} - } abstract class Task { - ID_GENERATOR : AtomicInteger {static} - id : int @@ -18,18 +9,27 @@ package com.iluwatar.threadpool { + getTimeMs() : int + toString() : String } + class CoffeeMakingTask { + - TIME_PER_CUP : int {static} + + CoffeeMakingTask(numCups : int) + + toString() : String + } + class Worker { + - task : Task + + Worker(task : Task) + + run() + } class PotatoPeelingTask { - TIME_PER_POTATO : int {static} + PotatoPeelingTask(numPotatoes : int) + toString() : String } - class CoffeeMakingTask { - - TIME_PER_CUP : int {static} - + CoffeeMakingTask(numCups : int) - + toString() : String + class App { + + App() + + main(args : String[]) {static} } } Worker --> "-task" Task -PotatoPeelingTask --|> Task CoffeeMakingTask --|> Task +PotatoPeelingTask --|> Task @enduml \ No newline at end of file diff --git a/tolerant-reader/etc/tolerant-reader.urm.puml b/tolerant-reader/etc/tolerant-reader.urm.puml index 9e2bc83b31b7..f203f8257b52 100644 --- a/tolerant-reader/etc/tolerant-reader.urm.puml +++ b/tolerant-reader/etc/tolerant-reader.urm.puml @@ -1,11 +1,5 @@ @startuml package com.iluwatar.tolerantreader { - class RainbowFishSerializer { - - RainbowFishSerializer() - + readV1(filename : String) : RainbowFish {static} - + writeV1(rainbowFish : RainbowFish, filename : String) {static} - + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} - } class RainbowFish { - age : int - lengthMeters : int @@ -18,6 +12,10 @@ package com.iluwatar.tolerantreader { + getName() : String + getWeightTons() : int } + class App { + + App() + + main(args : String[]) {static} + } class RainbowFishV2 { - angry : boolean - hungry : boolean @@ -29,9 +27,11 @@ package com.iluwatar.tolerantreader { + getHungry() : boolean + getSleeping() : boolean } - class App { - + App() - + main(args : String[]) {static} + class RainbowFishSerializer { + - RainbowFishSerializer() + + readV1(filename : String) : RainbowFish {static} + + writeV1(rainbowFish : RainbowFish, filename : String) {static} + + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} } } RainbowFishV2 --|> RainbowFish diff --git a/twin/etc/twin.urm.puml b/twin/etc/twin.urm.puml index b95325abb35f..b4bd52468e00 100644 --- a/twin/etc/twin.urm.puml +++ b/twin/etc/twin.urm.puml @@ -1,9 +1,10 @@ @startuml package com.iluwatar.twin { - class App { - + App() - + main(args : String[]) {static} - - waiting() {static} + abstract class GameItem { + + GameItem() + + click() {abstract} + + doDraw() {abstract} + + draw() } class BallItem { - isSuspended : boolean @@ -14,11 +15,10 @@ package com.iluwatar.twin { + move() + setTwin(twin : BallThread) } - abstract class GameItem { - + GameItem() - + click() {abstract} - + doDraw() {abstract} - + draw() + class App { + + App() + + main(args : String[]) {static} + - waiting() {static} } } BallItem --|> GameItem diff --git a/value-object/etc/value-object.urm.puml b/value-object/etc/value-object.urm.puml index 223f9195775c..6c81de017ee8 100644 --- a/value-object/etc/value-object.urm.puml +++ b/value-object/etc/value-object.urm.puml @@ -1,9 +1,5 @@ @startuml package com.iluwatar.value.object { - class App { - + App() - + main(args : String[]) {static} - } class HeroStat { - intelligence : int - luck : int @@ -17,5 +13,9 @@ package com.iluwatar.value.object { + toString() : String + valueOf(strength : int, intelligence : int, luck : int) : HeroStat {static} } + class App { + + App() + + main(args : String[]) {static} + } } @enduml \ No newline at end of file diff --git a/visitor/etc/visitor.urm.puml b/visitor/etc/visitor.urm.puml index 3f5689f71fc9..1f28e4e2f316 100644 --- a/visitor/etc/visitor.urm.puml +++ b/visitor/etc/visitor.urm.puml @@ -1,57 +1,57 @@ @startuml package com.iluwatar.visitor { + class Commander { + + Commander(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + interface UnitVisitor { + + visitCommander(Commander) {abstract} + + visitSergeant(Sergeant) {abstract} + + visitSoldier(Soldier) {abstract} + } class CommanderVisitor { + CommanderVisitor() + visitCommander(commander : Commander) + visitSergeant(sergeant : Sergeant) + visitSoldier(soldier : Soldier) } - class Sergeant { - + Sergeant(children : Unit[]) + class Soldier { + + Soldier(children : Unit[]) + accept(visitor : UnitVisitor) + toString() : String } - class Commander { - + Commander(children : Unit[]) - + accept(visitor : UnitVisitor) - + toString() : String + class App { + + App() + + main(args : String[]) {static} } abstract class Unit { - children : Unit[] + Unit(children : Unit[]) + accept(visitor : UnitVisitor) } - class Soldier { - + Soldier(children : Unit[]) - + accept(visitor : UnitVisitor) - + toString() : String - } - class SergeantVisitor { - + SergeantVisitor() + class SoldierVisitor { + + SoldierVisitor() + visitCommander(commander : Commander) + visitSergeant(sergeant : Sergeant) + visitSoldier(soldier : Soldier) } - interface UnitVisitor { - + visitCommander(Commander) {abstract} - + visitSergeant(Sergeant) {abstract} - + visitSoldier(Soldier) {abstract} - } - class App { - + App() - + main(args : String[]) {static} - } - class SoldierVisitor { - + SoldierVisitor() + class SergeantVisitor { + + SergeantVisitor() + visitCommander(commander : Commander) + visitSergeant(sergeant : Sergeant) + visitSoldier(soldier : Soldier) } + class Sergeant { + + Sergeant(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } } -CommanderVisitor ..|> UnitVisitor -Sergeant --|> Unit Commander --|> Unit +CommanderVisitor ..|> UnitVisitor Soldier --|> Unit -SergeantVisitor ..|> UnitVisitor SoldierVisitor ..|> UnitVisitor +SergeantVisitor ..|> UnitVisitor +Sergeant --|> Unit @enduml \ No newline at end of file From dbd605e3786eb0d90ded55436976784865328d53 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:04 +0100 Subject: [PATCH 184/207] Changes based on latest code review --- .../com/iluwatar/event/asynchronous/App.java | 29 +++++++++++-------- .../iluwatar/event/asynchronous/Event.java | 17 ++++++++++- .../event/asynchronous/EventManager.java | 13 ++++++--- .../src/main/java/config.properties | 2 +- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index f951af07ca04..65ae02e56396 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -138,11 +138,10 @@ public void runInteractiveMode() { Scanner s = new Scanner(System.in); int option = -1; - while (option != 5) { + while (option != 4) { System.out.println("Hello. Would you like to boil some eggs?"); - System.out.println( - "(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); - System.out.print("Choose [1,2,3,4,5]: "); + System.out.println("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT"); + System.out.print("Choose [1,2,3,4]: "); option = s.nextInt(); if (option == 1) { @@ -181,16 +180,22 @@ public void runInteractiveMode() { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Which egg?: "); - int eventId = s.nextInt(); - try { - eventManager.status(eventId); - } catch (EventDoesNotExistException e) { - System.out.println(e.getMessage()); + s.nextLine(); + System.out.print("Just one egg (O) OR all of them (A) ?: "); + String eggChoice = s.nextLine(); + + if (eggChoice.equalsIgnoreCase("O")) { + System.out.print("Which egg?: "); + int eventId = s.nextInt(); + try { + eventManager.status(eventId); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (eggChoice.equalsIgnoreCase("A")) { + eventManager.statusOfAllEvents(); } } else if (option == 4) { - eventManager.statusOfAllEvents(); - } else if (option == 5) { eventManager.shutdown(); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 1cb04acdc966..c2e14ad68277 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -25,13 +25,25 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; + private boolean isSynchronous; private Thread thread; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(final int eventId, final int eventTime) { + /** + * + * @param eventId event ID + * @param eventTime event time + * @param isSynchronous is of synchronous type + */ + public Event(final int eventId, final int eventTime, final boolean isSynchronous) { this.eventId = eventId; this.eventTime = eventTime; + this.isSynchronous = isSynchronous; + } + + public boolean isSynchronous() { + return isSynchronous; } @Override @@ -42,6 +54,9 @@ public void start() { @Override public void stop() { + if (null == thread) { + return; + } thread.interrupt(); } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index e65816cec5af..dae995e382ab 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -61,11 +61,12 @@ public EventManager() { */ public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } + + int eventId = createEvent(eventTime, true); currentlyRunningSyncEvent = eventId; return eventId; @@ -80,10 +81,11 @@ public int create(int eventTime) * @throws LongRunningEventException Long running events are not allowed in the app. */ public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - return createEvent(eventTime); + return createEvent(eventTime, false); } - private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + private int createEvent(int eventTime, boolean isSynchronous) + throws MaxNumOfEventsAllowedException, LongRunningEventException { if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } @@ -95,7 +97,7 @@ private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, Lo int newEventId = generateId(); - Event newEvent = new Event(newEventId, eventTime); + Event newEvent = new Event(newEventId, eventTime, isSynchronous); newEvent.addListener(this); eventPool.put(newEventId, newEvent); @@ -194,6 +196,9 @@ private int generateId() { @Override public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); + if (eventPool.get(eventId).isSynchronous()) { + currentlyRunningSyncEvent = -1; + } eventPool.remove(eventId); } diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index edbe90e0544e..7216f665d8a3 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=NO \ No newline at end of file +INTERACTIVE_MODE=YES \ No newline at end of file From 371b262a5135484d83efb0666269af0a55c6f185 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:23 +0100 Subject: [PATCH 185/207] Changes based on latest code review --- event-asynchronous/src/main/java/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index 7216f665d8a3..edbe90e0544e 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 6ed842e58bab97bc7eebe47575838a0b1f0910d7 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 09:35:17 +0200 Subject: [PATCH 186/207] Caching pattern: Refactor shutdown hook to use method reference --- caching/src/main/java/com/iluwatar/caching/AppManager.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 2967c759f7b7..13ef4d4dd5aa 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -64,12 +64,7 @@ public static void initDb(boolean useMongoDb) { public static void initCachingPolicy(CachingPolicy policy) { cachingPolicy = policy; if (cachingPolicy == CachingPolicy.BEHIND) { - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override - public void run() { - CacheStore.flushCache(); - } - })); + Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache)); } CacheStore.clearCache(); } From 865f788612b4c5fd7fd978e346c47174750f8af4 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 09:38:23 +0200 Subject: [PATCH 187/207] Caching pattern: Refactor LRU cache to avoid NPE and unnecessary cache lookup --- caching/src/main/java/com/iluwatar/caching/LruCache.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 5c5549afd9d7..4527b5548c08 100644 --- a/caching/src/main/java/com/iluwatar/caching/LruCache.java +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -136,10 +136,11 @@ public boolean contains(String userId) { * Invalidate cache for user */ public void invalidate(String userId) { - System.out.println("# " + userId + " has been updated! Removing older version from cache..."); - Node toBeRemoved = cache.get(userId); - remove(toBeRemoved); - cache.remove(userId); + Node toBeRemoved = cache.remove(userId); + if (toBeRemoved != null) { + System.out.println("# " + userId + " has been updated! Removing older version from cache..."); + remove(toBeRemoved); + } } public boolean isFull() { From e3355d76d1930f935d902f54d4cf7fae4f94bd24 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 20:33:24 +0200 Subject: [PATCH 188/207] Caching pattern: Style fix for null check --- .../src/main/java/com/iluwatar/caching/CacheStore.java | 4 ++-- caching/src/main/java/com/iluwatar/caching/DbManager.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index 5903f82193e9..dd425645e420 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -40,7 +40,7 @@ private CacheStore() { * Init cache capacity */ public static void initCapacity(int capacity) { - if (null == cache) { + if (cache == null) { cache = new LruCache(capacity); } else { cache.setCapacity(capacity); @@ -121,7 +121,7 @@ public static void writeBehind(UserAccount userAccount) { * Clears cache */ public static void clearCache() { - if (null != cache) { + if (cache != null) { cache.clear(); } } diff --git a/caching/src/main/java/com/iluwatar/caching/DbManager.java b/caching/src/main/java/com/iluwatar/caching/DbManager.java index c12461d0c690..455302106b57 100644 --- a/caching/src/main/java/com/iluwatar/caching/DbManager.java +++ b/caching/src/main/java/com/iluwatar/caching/DbManager.java @@ -82,7 +82,7 @@ public static UserAccount readFromDb(String userId) { } return null; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { @@ -106,7 +106,7 @@ public static void writeToDb(UserAccount userAccount) { virtualDB.put(userAccount.getUserId(), userAccount); return; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { @@ -126,7 +126,7 @@ public static void updateDb(UserAccount userAccount) { virtualDB.put(userAccount.getUserId(), userAccount); return; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { @@ -148,7 +148,7 @@ public static void upsertDb(UserAccount userAccount) { virtualDB.put(userAccount.getUserId(), userAccount); return; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { From b31edda3cf9b376423e1a6cec0b7803a9597e522 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 20:41:26 +0200 Subject: [PATCH 189/207] Caching pattern: Implementation of Cache-Aside pattern --- .../main/java/com/iluwatar/caching/App.java | 23 +++++++++++++++ .../java/com/iluwatar/caching/AppManager.java | 29 +++++++++++++++++++ .../java/com/iluwatar/caching/CacheStore.java | 21 ++++++++++++++ .../com/iluwatar/caching/CachingPolicy.java | 2 +- .../com/iluwatar/caching/CachingTest.java | 5 ++++ 5 files changed, 79 insertions(+), 1 deletion(-) diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 8e5a84085ff9..4a3a9ab42f02 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -74,6 +74,7 @@ public static void main(String[] args) { app.useReadAndWriteThroughStrategy(); app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteBehindStrategy(); + app.useCacheAsideStategy(); } /** @@ -136,4 +137,26 @@ public void useReadThroughAndWriteBehindStrategy() { AppManager.find("004"); System.out.println(AppManager.printCacheContent()); } + + /** + * Cache-Aside + */ + public void useCacheAsideStategy() { + System.out.println("# CachingPolicy.ASIDE"); + AppManager.initCachingPolicy(CachingPolicy.ASIDE); + System.out.println(AppManager.printCacheContent()); + + UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food."); + UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats."); + UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); + AppManager.save(userAccount3); + AppManager.save(userAccount4); + AppManager.save(userAccount5); + + System.out.println(AppManager.printCacheContent()); + AppManager.find("003"); + System.out.println(AppManager.printCacheContent()); + AppManager.find("004"); + System.out.println(AppManager.printCacheContent()); + } } diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 13ef4d4dd5aa..2d6a424a35b4 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -81,6 +81,8 @@ public static UserAccount find(String userId) { return CacheStore.readThrough(userId); } else if (cachingPolicy == CachingPolicy.BEHIND) { return CacheStore.readThroughWithWriteBackPolicy(userId); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + return findAside(userId); } return null; } @@ -95,10 +97,37 @@ public static void save(UserAccount userAccount) { CacheStore.writeAround(userAccount); } else if (cachingPolicy == CachingPolicy.BEHIND) { CacheStore.writeBehind(userAccount); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + saveAside(userAccount); } } public static String printCacheContent() { return CacheStore.print(); } + + /** + * Cache-Aside save user account helper + */ + private static void saveAside(UserAccount userAccount) { + DbManager.updateDb(userAccount); + CacheStore.invalidate(userAccount.getUserId()); + } + + /** + * Cache-Aside find user account helper + */ + private static UserAccount findAside(String userId) { + UserAccount userAccount = CacheStore.get(userId); + if (userAccount != null) { + return userAccount; + } + + userAccount = DbManager.readFromDb(userId); + if (userAccount != null) { + CacheStore.set(userId, userAccount); + } + + return userAccount; + } } diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index dd425645e420..c2b95a1bdb55 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -153,4 +153,25 @@ public static String print() { sb.append("----\n"); return sb.toString(); } + + /** + * Delegate to backing cache store + */ + public static UserAccount get(String userId) { + return cache.get(userId); + } + + /** + * Delegate to backing cache store + */ + public static void set(String userId, UserAccount userAccount) { + cache.set(userId, userAccount); + } + + /** + * Delegate to backing cache store + */ + public static void invalidate(String userId) { + cache.invalidate(userId); + } } diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java index 490113baa3d9..0c907fe88487 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -28,7 +28,7 @@ * */ public enum CachingPolicy { - THROUGH("through"), AROUND("around"), BEHIND("behind"); + THROUGH("through"), AROUND("around"), BEHIND("behind"), ASIDE("aside"); private String policy; diff --git a/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java index 19262a3b6e35..9d2299902a8c 100644 --- a/caching/src/test/java/com/iluwatar/caching/CachingTest.java +++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java @@ -60,4 +60,9 @@ public void testReadThroughAndWriteAroundStrategy() { public void testReadThroughAndWriteBehindStrategy() { app.useReadThroughAndWriteBehindStrategy(); } + + @Test + public void testCacheAsideStrategy() { + app.useCacheAsideStategy(); + } } From c5e6dcc11b59db979751c07316fda6daccb9730d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 30 Sep 2016 19:38:28 +0300 Subject: [PATCH 190/207] Reached milestone 1.13.0 --- abstract-document/pom.xml | 2 +- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- aggregator-microservices/aggregator-service/pom.xml | 2 +- aggregator-microservices/information-microservice/pom.xml | 2 +- aggregator-microservices/inventory-microservice/pom.xml | 2 +- aggregator-microservices/pom.xml | 2 +- api-gateway/api-gateway-service/pom.xml | 2 +- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- caching/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- data-mapper/pom.xml | 2 +- decorator/pom.xml | 2 +- delegation/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- event-driven-architecture/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-kit/pom.xml | 2 +- factory-method/pom.xml | 2 +- feature-toggle/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- hexagonal/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- monad/pom.xml | 2 +- monostate/pom.xml | 2 +- multiton/pom.xml | 2 +- mute-idiom/pom.xml | 2 +- mutex/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- page-object/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- producer-consumer/pom.xml | 2 +- promise/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- publish-subscribe/pom.xml | 2 +- reactor/pom.xml | 2 +- reader-writer-lock/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- semaphore/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- twin/pom.xml | 2 +- value-object/pom.xml | 2 +- visitor/pom.xml | 2 +- 92 files changed, 95 insertions(+), 95 deletions(-) diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index b7a348d26617..be9aa68b9e04 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 abstract-document diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index ec0f700a53cc..1d54f0c861c5 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index 2c99796f4098..d8414f85de0b 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 adapter diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index 169d0da94ff3..46d7d72c70bf 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index 9865403446f3..5d2d97fe6fe8 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index f8844dd39774..8869596f8dd8 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index 6e9496ba3c10..85d5ebd92b05 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 aggregator-microservices diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index 87a1ebb5ef96..e24613b667ea 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 api-gateway-service diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index c29932fae1e2..18771f343501 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 48bfff9c3ca2..9222751d98bd 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index 24e9663aa7c7..a5d5aad3795f 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index d912df965908..156804c5fb5c 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index a7a3883c8cb8..d1a3f0e88845 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 bridge diff --git a/builder/pom.xml b/builder/pom.xml index 6c171bef4e9d..e63f68b960f0 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index c6f7e0c3715d..72ad13d0376b 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 business-delegate diff --git a/caching/pom.xml b/caching/pom.xml index c20842a892a2..3aa5c689e99d 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 caching diff --git a/callback/pom.xml b/callback/pom.xml index 4fe17d1431cb..fa8b614785eb 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 callback diff --git a/chain/pom.xml b/chain/pom.xml index ee3b92401bb1..7550e61c0972 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 chain diff --git a/command/pom.xml b/command/pom.xml index a27a56e54809..cfe40ce56650 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 command diff --git a/composite/pom.xml b/composite/pom.xml index ace29d8d13b7..aace20b3a6b8 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 composite diff --git a/dao/pom.xml b/dao/pom.xml index f64eff8bc251..85338a2636f3 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 dao diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 7d1b8346991a..80679127f75e 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 data-mapper diff --git a/decorator/pom.xml b/decorator/pom.xml index 7ba2a4ee8029..9991fdfe352b 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 decorator diff --git a/delegation/pom.xml b/delegation/pom.xml index 2fca225b83ea..ccfe133480d0 100644 --- a/delegation/pom.xml +++ b/delegation/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index 88ccdd2d4e2c..e04add78bece 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 239bcf87098f..882ac218ee56 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -27,7 +27,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 4f31b2e7e51d..9be737c4c8a3 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index b7de6e01b1b5..91294b125b93 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 event-aggregator diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml index 99ed39891ffd..972b4982c62a 100644 --- a/event-driven-architecture/pom.xml +++ b/event-driven-architecture/pom.xml @@ -31,7 +31,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 event-driven-architecture diff --git a/execute-around/pom.xml b/execute-around/pom.xml index 60bb6d0ab912..e46835d68db9 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 execute-around diff --git a/facade/pom.xml b/facade/pom.xml index e0e0f4a4d388..db7de2cd698c 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 facade diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml index 6c936de5c04b..00f9285d510b 100644 --- a/factory-kit/pom.xml +++ b/factory-kit/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 factory-kit diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 5081ecf9960e..8d720eb2826c 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 factory-method diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 78c182af9d03..6db201fb3e49 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index ca5e115d09a3..0dfe5d7a9a50 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index c07cca157ed1..27ee4f408c54 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index 959fa5548103..a153f97ccf2d 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index 2a448cffea85..ba6dcbe832c6 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index 076dfb44daa9..afe4e8c74e55 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 half-sync-half-async diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index ce1a7049a8f3..004554e6b591 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 hexagonal diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index 6bb6f95cc5e1..d30d28e1e796 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 9f5dd31f30c7..051ec1c15671 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index 484030a33fb9..98c9f883409e 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 iterator diff --git a/layers/pom.xml b/layers/pom.xml index bf155b655f69..5af14908baca 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index fe6d44c78d90..056d3b579c94 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index a6cdd028eea6..a01cf152ab9c 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 mediator diff --git a/memento/pom.xml b/memento/pom.xml index a320f186cb40..7c58fb361f8a 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index cbeba7a75c98..2b3c2e01e262 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 3c01f6e8dd22..32b9d5b75e12 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 0aad02b27772..6a43352804bf 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 model-view-presenter model-view-presenter diff --git a/monad/pom.xml b/monad/pom.xml index 7f128272d5db..4e9b368420be 100644 --- a/monad/pom.xml +++ b/monad/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 monad diff --git a/monostate/pom.xml b/monostate/pom.xml index 64ee52abe3cd..80f8f919bf8e 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 monostate diff --git a/multiton/pom.xml b/multiton/pom.xml index 0b835ed4d5c0..1d097a0649dd 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 multiton diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml index c138c5ed4fea..00a4dff5d853 100644 --- a/mute-idiom/pom.xml +++ b/mute-idiom/pom.xml @@ -21,7 +21,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 mute-idiom diff --git a/mutex/pom.xml b/mutex/pom.xml index 852006cd46cc..529652ea644c 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 mutex diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index 1194166941d8..763440cc4fd3 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index 46b28146895f..ca2f4af8602b 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index dccaf64a3f16..f7bf1a1b5824 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index d416f2a7219e..bc99d852cd7f 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 naked-objects @@ -367,17 +367,17 @@ ${project.groupId} naked-objects-dom - 1.13.0-SNAPSHOT + 1.13.0 ${project.groupId} naked-objects-fixture - 1.13.0-SNAPSHOT + 1.13.0 ${project.groupId} naked-objects-webapp - 1.13.0-SNAPSHOT + 1.13.0 diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index 762438aca09f..64aa036e356f 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index b0adad1af47f..2cd0a14f48e4 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index 666242d1d34b..dcb293bb3c51 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 object-pool diff --git a/observer/pom.xml b/observer/pom.xml index 847a7ea89539..8fb01821457b 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 observer diff --git a/page-object/pom.xml b/page-object/pom.xml index e6f888b05d4e..93a81d6486ba 100644 --- a/page-object/pom.xml +++ b/page-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 page-object diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 9717f5e13cda..5cf56e3e9231 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 poison-pill diff --git a/pom.xml b/pom.xml index fcf1671c8b97..ebb75aa9d029 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 4.0.0 com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 pom 2014 diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 20484245bdd5..b26d31420e65 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 private-class-data diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index a72aa2c960e7..da849a7531e2 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 producer-consumer diff --git a/promise/pom.xml b/promise/pom.xml index ca12515ee846..e8b129b001d1 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 promise diff --git a/property/pom.xml b/property/pom.xml index 1e9d0f0c2b16..fc000c41b6aa 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 property diff --git a/prototype/pom.xml b/prototype/pom.xml index e8bb4303a9ef..308bf91db8c0 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index f16736a2c27d..8b4191e981db 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 proxy diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml index 6db6b35384b5..73476a96623a 100644 --- a/publish-subscribe/pom.xml +++ b/publish-subscribe/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 publish-subscribe diff --git a/reactor/pom.xml b/reactor/pom.xml index c06584c6f05c..62ffe374d6cb 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 reactor diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index 2df3d5b147a9..417877b9c6da 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 reader-writer-lock diff --git a/repository/pom.xml b/repository/pom.xml index 56a448ec57c8..304a620ee6c3 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index 7b411f46c790..549fe3cbd4d1 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 resource-acquisition-is-initialization diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 1b3bf8b5d853..5f0f1c9cd9be 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 semaphore diff --git a/servant/pom.xml b/servant/pom.xml index c235b005cd32..145b726c39ba 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index 23d78859591b..da8fe17fecb4 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index 0f17b3dcc19a..e0eff0e38403 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index ab9405c5e750..d549025fd923 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 singleton diff --git a/specification/pom.xml b/specification/pom.xml index 03c66540d54c..e3fcdd86e258 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 specification diff --git a/state/pom.xml b/state/pom.xml index 134fbabe180f..3c481c4e10b2 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 96098eabca56..7d39dad5704e 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index e6fa58081df2..3902a1871e04 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index ee533df8f44b..1eeb183698f6 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index e7fa43103ef2..19031bc23480 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index e5dd3ba8874c..101673c9fef3 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 tolerant-reader diff --git a/twin/pom.xml b/twin/pom.xml index 1eb854b87384..df839bf58988 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 twin diff --git a/value-object/pom.xml b/value-object/pom.xml index ec8de16811b7..c2012093a4d4 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 value-object diff --git a/visitor/pom.xml b/visitor/pom.xml index 54a90e1840f4..39c7b45e5953 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 visitor From 4ca205c03cc03ea5bc80fe27c0a4594f0d087582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 30 Sep 2016 19:40:28 +0300 Subject: [PATCH 191/207] Set version number for next development iteration --- abstract-document/pom.xml | 2 +- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- aggregator-microservices/aggregator-service/pom.xml | 2 +- aggregator-microservices/information-microservice/pom.xml | 2 +- aggregator-microservices/inventory-microservice/pom.xml | 2 +- aggregator-microservices/pom.xml | 2 +- api-gateway/api-gateway-service/pom.xml | 2 +- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- caching/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- data-mapper/pom.xml | 2 +- decorator/pom.xml | 2 +- delegation/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- event-driven-architecture/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-kit/pom.xml | 2 +- factory-method/pom.xml | 2 +- feature-toggle/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- hexagonal/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- monad/pom.xml | 2 +- monostate/pom.xml | 2 +- multiton/pom.xml | 2 +- mute-idiom/pom.xml | 2 +- mutex/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- page-object/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- producer-consumer/pom.xml | 2 +- promise/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- publish-subscribe/pom.xml | 2 +- reactor/pom.xml | 2 +- reader-writer-lock/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- semaphore/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- twin/pom.xml | 2 +- value-object/pom.xml | 2 +- visitor/pom.xml | 2 +- 92 files changed, 95 insertions(+), 95 deletions(-) diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index be9aa68b9e04..1ab6d5a2b315 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT abstract-document diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index 1d54f0c861c5..5c3d4d16abf7 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index d8414f85de0b..83a930653c34 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT adapter diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index 46d7d72c70bf..520aefaa81a5 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index 5d2d97fe6fe8..4cd1916c60ce 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index 8869596f8dd8..1bf655b45ee2 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index 85d5ebd92b05..cedc35eec819 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 aggregator-microservices diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index e24613b667ea..e2ab9e6d9a4e 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 api-gateway-service diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index 18771f343501..db25b947b721 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 9222751d98bd..94f91cabb2d2 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index a5d5aad3795f..ce47e9db0e58 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index 156804c5fb5c..e374b4753554 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index d1a3f0e88845..4960dbd0831b 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT bridge diff --git a/builder/pom.xml b/builder/pom.xml index e63f68b960f0..030990fa2b01 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 72ad13d0376b..2b0ce9119386 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT business-delegate diff --git a/caching/pom.xml b/caching/pom.xml index 3aa5c689e99d..07347e5c758b 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT caching diff --git a/callback/pom.xml b/callback/pom.xml index fa8b614785eb..70dd8e5e278a 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT callback diff --git a/chain/pom.xml b/chain/pom.xml index 7550e61c0972..5593d3e73e50 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT chain diff --git a/command/pom.xml b/command/pom.xml index cfe40ce56650..406c6d100123 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT command diff --git a/composite/pom.xml b/composite/pom.xml index aace20b3a6b8..48d693d2fc86 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT composite diff --git a/dao/pom.xml b/dao/pom.xml index 85338a2636f3..e414f2b27c06 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT dao diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 80679127f75e..eb342e9eb7a7 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT data-mapper diff --git a/decorator/pom.xml b/decorator/pom.xml index 9991fdfe352b..36c3eef01ab4 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT decorator diff --git a/delegation/pom.xml b/delegation/pom.xml index ccfe133480d0..ed5f6608eb77 100644 --- a/delegation/pom.xml +++ b/delegation/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index e04add78bece..ab4619b63783 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 882ac218ee56..27c97479a428 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -27,7 +27,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 9be737c4c8a3..5093cced82fa 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 91294b125b93..29d99ca2bbd0 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT event-aggregator diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml index 972b4982c62a..149ca7409da7 100644 --- a/event-driven-architecture/pom.xml +++ b/event-driven-architecture/pom.xml @@ -31,7 +31,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT event-driven-architecture diff --git a/execute-around/pom.xml b/execute-around/pom.xml index e46835d68db9..115c201cbe17 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT execute-around diff --git a/facade/pom.xml b/facade/pom.xml index db7de2cd698c..7816d51e656e 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT facade diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml index 00f9285d510b..32705cc466a7 100644 --- a/factory-kit/pom.xml +++ b/factory-kit/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT factory-kit diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 8d720eb2826c..74dc91a02419 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT factory-method diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 6db201fb3e49..eebc433e80c5 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index 0dfe5d7a9a50..b438103e489d 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index 27ee4f408c54..50f5d395a4d4 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index a153f97ccf2d..eb168c4865eb 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index ba6dcbe832c6..c08ed8d0fae1 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index afe4e8c74e55..d22e65a5e402 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT half-sync-half-async diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 004554e6b591..c489c45c498f 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT hexagonal diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index d30d28e1e796..487ac1c14698 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 051ec1c15671..e2c8f4d4dcca 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index 98c9f883409e..6d589b4a7132 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT iterator diff --git a/layers/pom.xml b/layers/pom.xml index 5af14908baca..811f5e050f7e 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index 056d3b579c94..d70587395183 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index a01cf152ab9c..e3b227237fbd 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT mediator diff --git a/memento/pom.xml b/memento/pom.xml index 7c58fb361f8a..3dc7e5f52602 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index 2b3c2e01e262..7c1825b5e728 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 32b9d5b75e12..38e756313e47 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 6a43352804bf..6fb7ab31e969 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT model-view-presenter model-view-presenter diff --git a/monad/pom.xml b/monad/pom.xml index 4e9b368420be..482cf4e4d5be 100644 --- a/monad/pom.xml +++ b/monad/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT monad diff --git a/monostate/pom.xml b/monostate/pom.xml index 80f8f919bf8e..d42870a17b9a 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT monostate diff --git a/multiton/pom.xml b/multiton/pom.xml index 1d097a0649dd..28e208d0ca97 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT multiton diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml index 00a4dff5d853..cae190e868ed 100644 --- a/mute-idiom/pom.xml +++ b/mute-idiom/pom.xml @@ -21,7 +21,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT mute-idiom diff --git a/mutex/pom.xml b/mutex/pom.xml index 529652ea644c..b53587aa375f 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT mutex diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index 763440cc4fd3..7e16ca000b8d 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index ca2f4af8602b..f31b10fe2337 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index f7bf1a1b5824..547e88268118 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index bc99d852cd7f..344a234acbdb 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT naked-objects @@ -367,17 +367,17 @@ ${project.groupId} naked-objects-dom - 1.13.0 + 1.14.0-SNAPSHOT ${project.groupId} naked-objects-fixture - 1.13.0 + 1.14.0-SNAPSHOT ${project.groupId} naked-objects-webapp - 1.13.0 + 1.14.0-SNAPSHOT diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index 64aa036e356f..1a729ade9a3c 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index 2cd0a14f48e4..832337772688 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index dcb293bb3c51..927570b66a36 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT object-pool diff --git a/observer/pom.xml b/observer/pom.xml index 8fb01821457b..48909e1aa84c 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT observer diff --git a/page-object/pom.xml b/page-object/pom.xml index 93a81d6486ba..207d04092d98 100644 --- a/page-object/pom.xml +++ b/page-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT page-object diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 5cf56e3e9231..432f1954754a 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT poison-pill diff --git a/pom.xml b/pom.xml index ebb75aa9d029..144b8efffc4c 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 4.0.0 com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT pom 2014 diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index b26d31420e65..1dc545cb162a 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT private-class-data diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index da849a7531e2..5ac018f512fc 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT producer-consumer diff --git a/promise/pom.xml b/promise/pom.xml index e8b129b001d1..30a719f13131 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT promise diff --git a/property/pom.xml b/property/pom.xml index fc000c41b6aa..af0c9f2886a9 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT property diff --git a/prototype/pom.xml b/prototype/pom.xml index 308bf91db8c0..d0c03c0b6f57 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index 8b4191e981db..7484fc47e751 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT proxy diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml index 73476a96623a..de5b6068b08e 100644 --- a/publish-subscribe/pom.xml +++ b/publish-subscribe/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT publish-subscribe diff --git a/reactor/pom.xml b/reactor/pom.xml index 62ffe374d6cb..5bb577c42421 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT reactor diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index 417877b9c6da..f10acb8d717d 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT reader-writer-lock diff --git a/repository/pom.xml b/repository/pom.xml index 304a620ee6c3..45f735b7a92d 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index 549fe3cbd4d1..ef5de75326da 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT resource-acquisition-is-initialization diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 5f0f1c9cd9be..4ee0edb9f7c2 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT semaphore diff --git a/servant/pom.xml b/servant/pom.xml index 145b726c39ba..1e99bc09b2b5 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index da8fe17fecb4..830ced4f603a 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index e0eff0e38403..894908f927a5 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index d549025fd923..2ba9fe6d4b7f 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT singleton diff --git a/specification/pom.xml b/specification/pom.xml index e3fcdd86e258..49e56f3c1f35 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT specification diff --git a/state/pom.xml b/state/pom.xml index 3c481c4e10b2..ec932532d624 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 7d39dad5704e..cd53119ffc0f 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index 3902a1871e04..e57f42e08f0c 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index 1eeb183698f6..2098a801ffc7 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index 19031bc23480..e1f9a4e49fd3 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index 101673c9fef3..9668b5fb3716 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT tolerant-reader diff --git a/twin/pom.xml b/twin/pom.xml index df839bf58988..56fe7bd8ab08 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT twin diff --git a/value-object/pom.xml b/value-object/pom.xml index c2012093a4d4..156d5b969825 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT value-object diff --git a/visitor/pom.xml b/visitor/pom.xml index 39c7b45e5953..e1d3ca6a1d43 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT visitor From 9512f3ec701a8236ee2bf07bedd9ff9cb15aa154 Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Mon, 3 Oct 2016 21:59:36 +0200 Subject: [PATCH 192/207] Closes #436. Adds criticism to service locator pattern. --- service-locator/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service-locator/README.md b/service-locator/README.md index 31d82b13f8bd..75a00ca57137 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -33,6 +33,13 @@ improves the performance of application to great extent. * lookups of services are done quite frequently * large number of services are being used +## Consequences + +* Violates Interface Segregation Principle (ISP) by providing pattern consumers with an access +to a number of services that they don't potentially need. +* Creates hidden dependencies that can break the clients at runtime. +* Limits object composability by stopping the clients to specify needed dependencies for different objects instantiation. + ## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) From 12544caa759fa1ac0fec7bd5bde5dae9d7a4a082 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:05:11 +0100 Subject: [PATCH 193/207] Changes based on review feedback. --- event-asynchronous/README.md | 2 +- .../com/iluwatar/event/asynchronous/App.java | 12 ++--- .../iluwatar/event/asynchronous/Event.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 52 +++++++++++++++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index dde434abae74..ef35d0b383fc 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -3,7 +3,7 @@ layout: pattern title: Event-based Asynchronous folder: event-asynchronous permalink: /patterns/event-asynchronous/ -categories: Other +categories: Concurrency tags: - difficulty-intermediate - performance diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 65ae02e56396..5a2565940839 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -106,23 +106,23 @@ public void quickRun() { try { // Create an Asynchronous event. int aEventId = eventManager.createAsync(60); - System.out.println("Event [" + aEventId + "] has been created."); + System.out.println("Async Event [" + aEventId + "] has been created."); eventManager.start(aEventId); - System.out.println("Event [" + aEventId + "] has been started."); + System.out.println("Async Event [" + aEventId + "] has been started."); // Create a Synchronous event. int sEventId = eventManager.create(60); - System.out.println("Event [" + sEventId + "] has been created."); + System.out.println("Sync Event [" + sEventId + "] has been created."); eventManager.start(sEventId); - System.out.println("Event [" + sEventId + "] has been started."); + System.out.println("Sync Event [" + sEventId + "] has been started."); eventManager.status(aEventId); eventManager.status(sEventId); eventManager.cancel(aEventId); - System.out.println("Event [" + aEventId + "] has been stopped."); + System.out.println("Async Event [" + aEventId + "] has been stopped."); eventManager.cancel(sEventId); - System.out.println("Event [" + sEventId + "] has been stopped."); + System.out.println("Sync Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index c2e14ad68277..5e557fff2e0e 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -75,7 +75,7 @@ public void run() { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - Thread.sleep(5000); // Sleep for 5 seconds. + Thread.sleep(1000); // Sleep for 1 second. } catch (InterruptedException e) { return; } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 6565d5badbc4..92bacda99e09 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -79,4 +79,56 @@ public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException System.out.println(e.getMessage()); } } + + @Test + public void testFullSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int sEventId = eventManager.create(eventTime); + assertTrue(eventManager.getEventPool().size() == 1); + eventManager.start(sEventId); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + // properly. + while (System.currentTimeMillis() < endTime) { + } + + assertTrue(eventManager.getEventPool().size() == 0); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testFullAsynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int aEventId1 = eventManager.createAsync(eventTime); + int aEventId2 = eventManager.createAsync(eventTime); + int aEventId3 = eventManager.createAsync(eventTime); + assertTrue(eventManager.getEventPool().size() == 3); + + eventManager.start(aEventId1); + eventManager.start(aEventId2); + eventManager.start(aEventId3); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + // properly. + while (System.currentTimeMillis() < endTime) { + } + + assertTrue(eventManager.getEventPool().size() == 0); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } } From 622376e0fa3b1723b86853c4c8a882b6bd378cb2 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:16:23 +0100 Subject: [PATCH 194/207] Updated version snapshot to 1.14.0 --- event-asynchronous/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml index 60ab8f0aac2c..1bc4549d4aaa 100644 --- a/event-asynchronous/pom.xml +++ b/event-asynchronous/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.14.0-SNAPSHOT event-asynchronous From 8f1758c28f663f73ea79be8d2a1575fcc4d9fffe Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:46:16 +0100 Subject: [PATCH 195/207] Alter JUnit tests to run in lesser time. --- .../event/asynchronous/EventAsynchronousTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 92bacda99e09..213439203326 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -84,15 +84,16 @@ public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException public void testFullSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int sEventId = eventManager.create(eventTime); assertTrue(eventManager.getEventPool().size() == 1); eventManager.start(sEventId); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete - // properly. + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to + // complete + // properly. while (System.currentTimeMillis() < endTime) { } @@ -108,7 +109,7 @@ public void testFullSynchronousEvent() { public void testFullAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int aEventId1 = eventManager.createAsync(eventTime); int aEventId2 = eventManager.createAsync(eventTime); @@ -120,7 +121,7 @@ public void testFullAsynchronousEvent() { eventManager.start(aEventId3); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete // properly. while (System.currentTimeMillis() < endTime) { } From eea8785a22527899da48c56f64b63aa0db1df1b5 Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Tue, 4 Oct 2016 14:34:01 +0200 Subject: [PATCH 196/207] Fixes #437. Adds criticism to Singleton pattern. --- singleton/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/singleton/README.md b/singleton/README.md index e1bb42a45eab..38a05349b7fe 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -36,6 +36,11 @@ Use the Singleton pattern when * [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) +## Consequences + +* Violates Single Responsibility Principle (SRP) by controlling their own creation and lifecycle. +* Encourages using a global shared instance which prevents an object and resources used by this object from being deallocated. + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) From f114b5b957c8ef432a504b24a56efa36599a9017 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Sun, 9 Oct 2016 12:43:43 +0200 Subject: [PATCH 197/207] Caching pattern: Documentation and diagram --- caching/README.md | 1 + caching/etc/caching.png | Bin 56234 -> 114847 bytes caching/etc/caching.ucls | 190 ++++++++---------- .../main/java/com/iluwatar/caching/App.java | 23 ++- .../com/iluwatar/caching/CachingPolicy.java | 2 +- .../java/com/iluwatar/caching/LruCache.java | 3 - .../com/iluwatar/caching/UserAccount.java | 2 - 7 files changed, 101 insertions(+), 120 deletions(-) diff --git a/caching/README.md b/caching/README.md index 6432ffcea26e..b48d060f0012 100644 --- a/caching/README.md +++ b/caching/README.md @@ -27,3 +27,4 @@ Use the Caching pattern(s) when * [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) * [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) +* [Cache-Aside](https://msdn.microsoft.com/en-us/library/dn589799.aspx) diff --git a/caching/etc/caching.png b/caching/etc/caching.png index 6b3b2d0556c51b7c593028b2c9120a00c47716c1..b6ed703ab8b7dfce5e5d82f097dac534828fc93a 100644 GIT binary patch literal 114847 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

    $0ITcj(KRIbdLxc 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 diff --git a/caching/etc/caching.ucls b/caching/etc/caching.ucls index 815a62ec99ce..a058277ea2fd 100644 --- a/caching/etc/caching.ucls +++ b/caching/etc/caching.ucls @@ -1,106 +1,90 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 4a3a9ab42f02..1b1b5e1cac91 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -26,22 +26,23 @@ * * The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing * the resources immediately after their use. The resources retain their identity, are kept in some - * fast-access storage, and are re-used to avoid having to acquire them again. There are three main - * caching strategies/techniques in this pattern; each with their own pros and cons. They are: + * fast-access storage, and are re-used to avoid having to acquire them again. There are four main + * caching strategies/techniques in this pattern; each with their own pros and cons. They are; * write-through which writes data to the cache and DB in a single transaction, - * write-around which writes data immediately into the DB instead of the cache, and + * write-around which writes data immediately into the DB instead of the cache, * write-behind which writes data into the cache initially whilst the data is only - * written into the DB when the cache is full. The read-through strategy is also - * included in the mentioned three strategies -- returns data from the cache to the caller if - * it exists else queries from DB and stores it into the cache for future use. These - * strategies determine when the data in the cache should be written back to the backing store (i.e. - * Database) and help keep both data sources synchronized/up-to-date. This pattern can improve - * performance and also helps to maintain consistency between data held in the cache and the data in - * the underlying data store. + * written into the DB when the cache is full, and cache-aside which pushes the + * responsibility of keeping the data synchronized in both data sources to the application itself. + * The read-through strategy is also included in the mentioned four strategies -- + * returns data from the cache to the caller if it exists else queries from DB and + * stores it into the cache for future use. These strategies determine when the data in the cache + * should be written back to the backing store (i.e. Database) and help keep both data sources + * synchronized/up-to-date. This pattern can improve performance and also helps to maintain + * consistency between data held in the cache and the data in the underlying data store. *

    * In this example, the user account ({@link UserAccount}) entity is used as the underlying * application data. The cache itself is implemented as an internal (Java) data structure. It adopts - * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three + * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four * strategies are individually tested. The testing of the cache is restricted towards saving and * querying of user accounts from the underlying data store ( {@link DbManager}). The main class ( * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java index 0c907fe88487..5ad32f6993a5 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -24,7 +24,7 @@ /** * - * Enum class containing the three caching strategies implemented in the pattern. + * Enum class containing the four caching strategies implemented in the pattern. * */ public enum CachingPolicy { diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 4527b5548c08..00e27ccf1fc1 100644 --- a/caching/src/main/java/com/iluwatar/caching/LruCache.java +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -73,7 +73,6 @@ public UserAccount get(String userId) { } /** - * * Remove node from linked list. */ public void remove(Node node) { @@ -90,7 +89,6 @@ public void remove(Node node) { } /** - * * Move node to the front of the list. */ public void setHead(Node node) { @@ -161,7 +159,6 @@ public void clear() { } /** - * * Returns cache data in list form. */ public List getCacheDataInListForm() { diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java index e2444ad72769..657f12fa33f1 100644 --- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java +++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java @@ -23,9 +23,7 @@ package com.iluwatar.caching; /** - * * Entity class (stored in cache and DB) used in the application. - * */ public class UserAccount { private String userId; From 85060784a7758af064aa883d66f029dd292ea9e8 Mon Sep 17 00:00:00 2001 From: dzmitryh Date: Sat, 15 Oct 2016 14:27:15 +0300 Subject: [PATCH 198/207] End process logic clause has been corrected. --- .../async/method/invocation/ThreadAsyncExecutor.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 7f96d9ab7c6e..5a10232f0c56 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 @@ -57,12 +57,10 @@ public AsyncResult startProcess(Callable task, AsyncCallback callba @Override public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { - if (asyncResult.isCompleted()) { - return asyncResult.getValue(); - } else { + if (!asyncResult.isCompleted()) { asyncResult.await(); - return asyncResult.getValue(); } + return asyncResult.getValue(); } /** From 37b930c3b75ebc17ac46f6ce9f93ef1ed8435aeb Mon Sep 17 00:00:00 2001 From: dzmitryh Date: Sat, 15 Oct 2016 14:29:32 +0300 Subject: [PATCH 199/207] Unused import removed. --- .../async/method/invocation/ThreadAsyncExecutorTest.java | 1 - 1 file changed, 1 deletion(-) 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 b4a23222abe6..09126e99fe3d 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 @@ -31,7 +31,6 @@ import java.util.concurrent.ExecutionException; import static org.junit.Assert.*; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; import static org.mockito.internal.verification.VerificationModeFactory.times; From 986c529eb67e5eccd999e4bc13d268a8c5997fc7 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 21:29:03 +0100 Subject: [PATCH 200/207] Moved config into a separate dir --- event-asynchronous/src/main/java/config.properties | 1 - event-asynchronous/src/main/resources/config.properties | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/main/resources/config.properties diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties deleted file mode 100644 index edbe90e0544e..000000000000 --- a/event-asynchronous/src/main/java/config.properties +++ /dev/null @@ -1 +0,0 @@ -INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties new file mode 100644 index 000000000000..7216f665d8a3 --- /dev/null +++ b/event-asynchronous/src/main/resources/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=YES \ No newline at end of file From 70318123fe762d919eda221e08b0f7bf2db5b8dd Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 22:22:06 +0100 Subject: [PATCH 201/207] Changed config to non-interactive --- event-asynchronous/src/main/resources/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index 7216f665d8a3..edbe90e0544e 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 99677867c66a318f4f41e714a7ed867131cfcdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 18 Oct 2016 07:51:37 +0300 Subject: [PATCH 202/207] Event Based Asynchronous pattern: Add missing license header and puml diagram --- .../etc/event-asynchronous.urm.puml | 65 +++++++++++++++++++ .../src/main/resources/config.properties | 23 +++++++ 2 files changed, 88 insertions(+) create mode 100644 event-asynchronous/etc/event-asynchronous.urm.puml diff --git a/event-asynchronous/etc/event-asynchronous.urm.puml b/event-asynchronous/etc/event-asynchronous.urm.puml new file mode 100644 index 000000000000..c5b183187c24 --- /dev/null +++ b/event-asynchronous/etc/event-asynchronous.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.event.asynchronous { + class Event { + - eventId : int + - eventListener : ThreadCompleteListener + - eventTime : int + - isComplete : boolean + - isSynchronous : boolean + - thread : Thread + + Event(eventId : int, eventTime : int, isSynchronous : boolean) + + addListener(listener : ThreadCompleteListener) + - completed() + + isSynchronous() : boolean + + removeListener(listener : ThreadCompleteListener) + + run() + + start() + + status() + + stop() + } + interface ThreadCompleteListener { + + completedEventHandler(int) {abstract} + } + class EventManager { + + MAX_EVENT_TIME : int {static} + + MAX_ID : int {static} + + MAX_RUNNING_EVENTS : int {static} + + MIN_ID : int {static} + - currentlyRunningSyncEvent : int + - eventPool : Map + - rand : Random + + EventManager() + + cancel(eventId : int) + + completedEventHandler(eventId : int) + + create(eventTime : int) : int + + createAsync(eventTime : int) : int + - createEvent(eventTime : int, isSynchronous : boolean) : int + - generateId() : int + + getEventPool() : Map + + numOfCurrentlyRunningSyncEvent() : int + + shutdown() + + start(eventId : int) + + status(eventId : int) + + statusOfAllEvents() + } + class App { + + PROP_FILE_NAME : String {static} + ~ interactiveMode : boolean + + App() + + main(args : String[]) {static} + + quickRun() + + run() + + runInteractiveMode() + + setUp() + } + interface IEvent { + + start() {abstract} + + status() {abstract} + + stop() {abstract} + } +} +Event --> "-eventListener" ThreadCompleteListener +EventManager --+ Map +Event ..|> IEvent +EventManager ..|> ThreadCompleteListener +@enduml \ No newline at end of file diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index edbe90e0544e..d8bfa1f7e054 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + INTERACTIVE_MODE=NO \ No newline at end of file From b66e8ecef970858e7823e6319ac378ba7fadfaea Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Tue, 18 Oct 2016 14:18:47 +0200 Subject: [PATCH 203/207] Adds more criticism to Singleton pattern. --- service-locator/README.md | 1 - singleton/README.md | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/service-locator/README.md b/service-locator/README.md index 75a00ca57137..479c9ed0f942 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -38,7 +38,6 @@ improves the performance of application to great extent. * Violates Interface Segregation Principle (ISP) by providing pattern consumers with an access to a number of services that they don't potentially need. * Creates hidden dependencies that can break the clients at runtime. -* Limits object composability by stopping the clients to specify needed dependencies for different objects instantiation. ## Credits diff --git a/singleton/README.md b/singleton/README.md index 38a05349b7fe..4032ffaed03b 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -40,6 +40,8 @@ Use the Singleton pattern when * Violates Single Responsibility Principle (SRP) by controlling their own creation and lifecycle. * Encourages using a global shared instance which prevents an object and resources used by this object from being deallocated. +* Creates tightly coupled code that is difficult to test. +* Makes it almost impossible to subclass a Singleton. ## Credits From ffdaf2ec4717164f466294bd7c37427fe3cddcdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 19 Oct 2016 22:25:37 +0300 Subject: [PATCH 204/207] Add Travis instructions for SonarQube.com analysis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 613f737d7164..4cfffe701a7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ env: global: - GH_REF: github.com/iluwatar/java-design-patterns.git - secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg= + - secure: "eoWlW9GyTJY04P8K3pxayXwU9/hmptQg/LfirispQkV9YvmziCfSzXnatnBhNfud98sCzY8BScXnb+OWLTnjLKpId4rtEqb0aJ40Jc32cUKzgzFAUn7cNcDAbUIfyPAGVqyQqfj/11wYSADwWMMOPlW97ExUtoyiH2WenXuRHso=" before_install: - export DISPLAY=:99.0 @@ -17,6 +18,7 @@ install: after_success: - mvn clean test jacoco:report coveralls:report +- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=SONAR_TOKEN - bash update-ghpages.sh # use latest java version available instead of travis default From 19cb715d20f269eb00aa6569244bf090459eb6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 19 Oct 2016 23:08:51 +0300 Subject: [PATCH 205/207] Fix environment variable --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4cfffe701a7f..ba226ff5c20f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ install: after_success: - mvn clean test jacoco:report coveralls:report -- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=SONAR_TOKEN +- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=$SONAR_TOKEN - bash update-ghpages.sh # use latest java version available instead of travis default From 1c0278592703a20720bb00b7490d435819afffb2 Mon Sep 17 00:00:00 2001 From: Fabrice Bellingard Date: Sun, 23 Oct 2016 14:35:45 +0200 Subject: [PATCH 206/207] Add SonarQube.com badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1789398ec2df..214fa91ffb61 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Quality Gate](https://sonarqube.com/api/badges/gate?key=com.iluwatar%3Ajava-design-patterns)](https://sonarqube.com/dashboard/index/com.iluwatar%3Ajava-design-patterns) # Introduction From 124fd33da07de5ea5e10a69a237cdce157cc5bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 1 Nov 2016 21:31:54 +0200 Subject: [PATCH 207/207] Remove use of coveralls-maven-plugin (sonarqube.com covers this) --- .travis.yml | 1 - README.md | 1 - pom.xml | 40 ---------------------------------------- 3 files changed, 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba226ff5c20f..817b6635f021 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,6 @@ install: - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e after_success: -- mvn clean test jacoco:report coveralls:report - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=$SONAR_TOKEN - bash update-ghpages.sh diff --git a/README.md b/README.md index 214fa91ffb61..6eed9561b551 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ # Design patterns implemented in Java [![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns) -[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Quality Gate](https://sonarqube.com/api/badges/gate?key=com.iluwatar%3Ajava-design-patterns)](https://sonarqube.com/dashboard/index/com.iluwatar%3Ajava-design-patterns) diff --git a/pom.xml b/pom.xml index 538058a81988..b8440f66483b 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,6 @@ 1.4.190 4.12 3.0 - 4.0.0 0.7.2.201409121644 1.4 2.16.1 @@ -320,29 +319,10 @@ 1.8 - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - org.jacoco jacoco-maven-plugin ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - **com.steadystate* - - prepare-agent @@ -378,26 +358,6 @@ - - - org.jacoco - jacoco-maven-plugin - 0.7.5.201505241946 - - - - prepare-agent - - - - report - prepare-package - - report - - - - org.apache.maven.plugins maven-surefire-plugin

  • rOge6I`XBAL3}}erSA|U;!qiat9!h8E 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 4a069ee93a2efed1da6758bf21641833380090e1 Mon Sep 17 00:00:00 2001 From: Colin But Date: Sun, 3 Jul 2016 21:36:03 +0100 Subject: [PATCH 103/207] new class diagrams --- page-object/etc/page-object.png | Bin 0 -> 49696 bytes page-object/etc/page-object.ucls | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 page-object/etc/page-object.png create mode 100644 page-object/etc/page-object.ucls diff --git a/page-object/etc/page-object.png b/page-object/etc/page-object.png new file mode 100644 index 0000000000000000000000000000000000000000..4240b438efbb2179ff5efc7f1795d4a1b431e580 GIT binary patch literal 49696 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