From 665256ecc0abc9fad3cea85a1afd1efd75f37f2d Mon Sep 17 00:00:00 2001 From: tmcconville Date: Sat, 2 Apr 2016 18:25:13 -0500 Subject: [PATCH] #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 bb7f5701d..36eef6668 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 0ecbf7405..b475dfdb2 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 class ApiGateway { * 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 7bad0269b..29829c8d5 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 @@ package com.iluwatar.api.gateway; * *

* 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 8105b01b1..ba43f0997 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 @@ package com.iluwatar.api.gateway; * Encapsulates all of the information that a desktop client needs to display a product. */ public class DesktopProduct { - + /** + * The price of the product + */ private String price; + + /** + * The path to the image of the product + */ private String imagePath; public String getPrice() { 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 000000000..fd1ae635b --- /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 000000000..62d426e8d --- /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 3904f545d..000000000 --- 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 772e540df..ed986f774 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 @@ package com.iluwatar.api.gateway; * 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 000000000..2df566bb2 --- /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 000000000..67d984c31 --- /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 f64f1cb11..000000000 --- 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 000000000..81efb206b --- /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 00abdacb5..f434f8857 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 class ApiGatewayTest { */ @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 706f34b71..000000000 --- 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); - } -}