From c5de72fc00a1aae3fe61c4418ed84b036bcb2cc6 Mon Sep 17 00:00:00 2001 From: Ranga Rao Karanam Date: Sat, 9 Sep 2017 18:06:12 +0530 Subject: [PATCH] Step29 --- .../currency-conversion-service/pom.xml | 5 + .../CurrencyConversionServiceApplication.java | 2 + .../src/main/resources/application.properties | 3 +- .../currency-exchange-service/pom.xml | 5 + .../CurrencyExchangeServiceApplication.java | 2 + .../src/main/resources/application.properties | 4 +- .../netflix-eureka-naming-server/.DS_Store | Bin 0 -> 6148 bytes .../netflix-eureka-naming-server/pom.xml | 114 ++ .../NetflixEurekaNamingServerApplication.java | 14 + .../src/main/resources/application.properties | 6 + ...lixEurekaNamingServerApplicationTests.java | 16 + 03.microservices/step25.md | 1283 ++++++++++++++++ 03.microservices/step25.zip | Bin 0 -> 39413 bytes 03.microservices/step29.md | 1300 +++++++++++++++++ 03.microservices/step29.zip | Bin 0 -> 39563 bytes 15 files changed, 2752 insertions(+), 2 deletions(-) create mode 100644 03.microservices/netflix-eureka-naming-server/.DS_Store create mode 100644 03.microservices/netflix-eureka-naming-server/pom.xml create mode 100644 03.microservices/netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java create mode 100644 03.microservices/netflix-eureka-naming-server/src/main/resources/application.properties create mode 100644 03.microservices/netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java create mode 100644 03.microservices/step25.md create mode 100644 03.microservices/step25.zip create mode 100644 03.microservices/step29.md create mode 100644 03.microservices/step29.zip diff --git a/03.microservices/currency-conversion-service/pom.xml b/03.microservices/currency-conversion-service/pom.xml index dbf98db9..cff4927d 100644 --- a/03.microservices/currency-conversion-service/pom.xml +++ b/03.microservices/currency-conversion-service/pom.xml @@ -40,6 +40,11 @@ spring-cloud-starter-feign + + org.springframework.cloud + spring-cloud-starter-eureka + + org.springframework.cloud spring-cloud-starter-ribbon diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java index 6042ed2e..c53627b4 100644 --- a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") +@EnableDiscoveryClient public class CurrencyConversionServiceApplication { public static void main(String[] args) { diff --git a/03.microservices/currency-conversion-service/src/main/resources/application.properties b/03.microservices/currency-conversion-service/src/main/resources/application.properties index 769573e5..8831ae2b 100644 --- a/03.microservices/currency-conversion-service/src/main/resources/application.properties +++ b/03.microservices/currency-conversion-service/src/main/resources/application.properties @@ -1,3 +1,4 @@ spring.application.name=currency-conversion-service server.port=8100 -currency-exchange-service.ribbon.listOfServers=http://localhost:8000,http://localhost:8001 \ No newline at end of file +eureka.client.service-url.default-zone=http://localhost:8761/eureka +#currency-exchange-service.ribbon.listOfServers=http://localhost:8000,http://localhost:8001 \ No newline at end of file diff --git a/03.microservices/currency-exchange-service/pom.xml b/03.microservices/currency-exchange-service/pom.xml index 4427f086..5d60cefb 100644 --- a/03.microservices/currency-exchange-service/pom.xml +++ b/03.microservices/currency-exchange-service/pom.xml @@ -34,6 +34,11 @@ org.springframework.cloud spring-cloud-starter-config + + org.springframework.cloud + spring-cloud-starter-eureka + + org.springframework.boot spring-boot-starter-web diff --git a/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java index a2cfa381..2c3481b5 100644 --- a/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java +++ b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication +@EnableDiscoveryClient public class CurrencyExchangeServiceApplication { public static void main(String[] args) { diff --git a/03.microservices/currency-exchange-service/src/main/resources/application.properties b/03.microservices/currency-exchange-service/src/main/resources/application.properties index 78b15c1f..a3688a2e 100644 --- a/03.microservices/currency-exchange-service/src/main/resources/application.properties +++ b/03.microservices/currency-exchange-service/src/main/resources/application.properties @@ -2,4 +2,6 @@ spring.application.name=currency-exchange-service server.port=8000 spring.jpa.show-sql=true -spring.h2.console.enabled=true \ No newline at end of file +spring.h2.console.enabled=true + +eureka.client.service-url.default-zone=http://localhost:8761/eureka \ No newline at end of file diff --git a/03.microservices/netflix-eureka-naming-server/.DS_Store b/03.microservices/netflix-eureka-naming-server/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 + + 4.0.0 + + com.in28minutes.microservices + netflix-eureka-naming-server + 0.0.1-SNAPSHOT + jar + + netflix-eureka-naming-server + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + diff --git a/03.microservices/netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java b/03.microservices/netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java new file mode 100644 index 00000000..5b8f2fac --- /dev/null +++ b/03.microservices/netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java @@ -0,0 +1,14 @@ +package com.in28minutes.microservices.netflixeurekanamingserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +@EnableEurekaServer +public class NetflixEurekaNamingServerApplication { + + public static void main(String[] args) { + SpringApplication.run(NetflixEurekaNamingServerApplication.class, args); + } +} diff --git a/03.microservices/netflix-eureka-naming-server/src/main/resources/application.properties b/03.microservices/netflix-eureka-naming-server/src/main/resources/application.properties new file mode 100644 index 00000000..61d9e1bd --- /dev/null +++ b/03.microservices/netflix-eureka-naming-server/src/main/resources/application.properties @@ -0,0 +1,6 @@ + +spring.application.name=netflix-eureka-naming-server +server.port=8761 + +eureka.client.register-with-eureka=false +eureka.client.fetch-registry=false \ No newline at end of file diff --git a/03.microservices/netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java b/03.microservices/netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java new file mode 100644 index 00000000..265d4494 --- /dev/null +++ b/03.microservices/netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.microservices.netflixeurekanamingserver; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class NetflixEurekaNamingServerApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/03.microservices/step25.md b/03.microservices/step25.md new file mode 100644 index 00000000..27691f16 --- /dev/null +++ b/03.microservices/step25.md @@ -0,0 +1,1283 @@ + +## Complete Code Example + + +### /currency-conversion-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + currency-conversion-service + 0.0.1-SNAPSHOT + jar + + currency-conversion-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-feign + + + + org.springframework.cloud + spring-cloud-starter-ribbon + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionBean.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import java.math.BigDecimal; + +public class CurrencyConversionBean { + private Long id; + private String from; + private String to; + private BigDecimal conversionMultiple; + private BigDecimal quantity; + private BigDecimal totalCalculatedAmount; + private int port; + + public CurrencyConversionBean() { + + } + + public CurrencyConversionBean(Long id, String from, String to, BigDecimal conversionMultiple, BigDecimal quantity, + BigDecimal totalCalculatedAmount, int port) { + super(); + this.id = id; + this.from = from; + this.to = to; + this.conversionMultiple = conversionMultiple; + this.quantity = quantity; + this.totalCalculatedAmount = totalCalculatedAmount; + this.port = port; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public BigDecimal getConversionMultiple() { + return conversionMultiple; + } + + public void setConversionMultiple(BigDecimal conversionMultiple) { + this.conversionMultiple = conversionMultiple; + } + + public BigDecimal getQuantity() { + return quantity; + } + + public void setQuantity(BigDecimal quantity) { + this.quantity = quantity; + } + + public BigDecimal getTotalCalculatedAmount() { + return totalCalculatedAmount; + } + + public void setTotalCalculatedAmount(BigDecimal totalCalculatedAmount) { + this.totalCalculatedAmount = totalCalculatedAmount; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class CurrencyConversionController { + + @Autowired + private CurrencyExchangeServiceProxy proxy; + + @GetMapping("/currency-converter/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrency(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + // Feign - Problem 1 + Map uriVariables = new HashMap<>(); + uriVariables.put("from", from); + uriVariables.put("to", to); + + ResponseEntity responseEntity = new RestTemplate().getForEntity( + "http://localhost:8000/currency-exchange/from/{from}/to/{to}", CurrencyConversionBean.class, + uriVariables); + + CurrencyConversionBean response = responseEntity.getBody(); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + + @GetMapping("/currency-converter-feign/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrencyFeign(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + CurrencyConversionBean response = proxy.retrieveExchangeValue(from, to); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.feign.EnableFeignClients; + +@SpringBootApplication +@EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") +public class CurrencyConversionServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyConversionServiceApplication.class, args); + } +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +//@FeignClient(name="currency-exchange-service", url="localhost:8000") +@FeignClient(name="currency-exchange-service") +@RibbonClient(name="currency-exchange-service") +public interface CurrencyExchangeServiceProxy { + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public CurrencyConversionBean retrieveExchangeValue + (@PathVariable("from") String from, @PathVariable("to") String to); +} +``` +--- + +### /currency-conversion-service/src/main/resources/application.properties + +```properties +spring.application.name=currency-conversion-service +server.port=8100 +currency-exchange-service.ribbon.listOfServers=http://localhost:8000,http://localhost:8001 +``` +--- + +### /currency-conversion-service/src/test/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplicationTests.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CurrencyConversionServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /currency-exchange-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + currency-exchange-service + 0.0.1-SNAPSHOT + jar + + currency-exchange-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeController.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CurrencyExchangeController { + + @Autowired + private Environment environment; + + @Autowired + private ExchangeValueRepository repository; + + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public ExchangeValue retrieveExchangeValue + (@PathVariable String from, @PathVariable String to){ + + ExchangeValue exchangeValue = + repository.findByFromAndTo(from, to); + + exchangeValue.setPort( + Integer.parseInt(environment.getProperty("local.server.port"))); + + return exchangeValue; + } +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CurrencyExchangeServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyExchangeServiceApplication.class, args); + } +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/ExchangeValue.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import java.math.BigDecimal; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class ExchangeValue { + + @Id + private Long id; + + @Column(name="currency_from") + private String from; + + @Column(name="currency_to") + private String to; + + private BigDecimal conversionMultiple; + private int port; + + public ExchangeValue() { + + } + + + public ExchangeValue(Long id, String from, String to, BigDecimal conversionMultiple) { + super(); + this.id = id; + this.from = from; + this.to = to; + this.conversionMultiple = conversionMultiple; + } + + public Long getId() { + return id; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public BigDecimal getConversionMultiple() { + return conversionMultiple; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/ExchangeValueRepository.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ExchangeValueRepository extends + JpaRepository{ + ExchangeValue findByFromAndTo(String from, String to); +} +``` +--- + +### /currency-exchange-service/src/main/resources/application.properties + +```properties +spring.application.name=currency-exchange-service +server.port=8000 + +spring.jpa.show-sql=true +spring.h2.console.enabled=true +``` +--- + +### /currency-exchange-service/src/main/resources/data.sql + +``` +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10001,'USD','INR',65,0); +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10002,'EUR','INR',75,0); +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10003,'AUD','INR',25,0); +``` +--- + +### /currency-exchange-service/src/test/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplicationTests.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CurrencyExchangeServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /git-localconfig-repo/limits-service-dev.properties + +```properties +limits-service.minimum=1 +``` +--- + +### /git-localconfig-repo/limits-service-qa.properties + +```properties +limits-service.minimum=2 +limits-service.maximum=222 +``` +--- + +### /git-localconfig-repo/limits-service.properties + +```properties +limits-service.minimum=8 +limits-service.maximum=888 +``` +--- + +### /limits-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + limits-service + 0.0.1-SNAPSHOT + jar + + limits-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/bean/LimitConfiguration.java + +```java +package com.in28minutes.microservices.limitsservice.bean; + +public class LimitConfiguration { + private int maximum; + private int minimum; + + protected LimitConfiguration() { + + } + + public LimitConfiguration(int maximum, int minimum) { + super(); + this.maximum = maximum; + this.minimum = minimum; + } + + public int getMaximum() { + return maximum; + } + + public int getMinimum() { + return minimum; + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/Configuration.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties("limits-service") +public class Configuration { + + private int minimum; + private int maximum; + + public void setMinimum(int minimum) { + this.minimum = minimum; + } + + public void setMaximum(int maximum) { + this.maximum = maximum; + } + + public int getMinimum() { + return minimum; + } + + public int getMaximum() { + return maximum; + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.microservices.limitsservice.bean.LimitConfiguration; + +@RestController +public class LimitsConfigurationController { + + @Autowired + private Configuration configuration; + + @GetMapping("/limits") + public LimitConfiguration retrieveLimitsFromConfigurations() { + return new LimitConfiguration(configuration.getMaximum(), + configuration.getMinimum()); + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsServiceApplication.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LimitsServiceApplication { + public static void main(String[] args) { + SpringApplication.run(LimitsServiceApplication.class, args); + } +} +``` +--- + +### /limits-service/src/main/resources/bootstrap.properties + +```properties +spring.application.name=limits-service +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=qa +``` +--- + +### /limits-service/src/test/java/com/in28minutes/microservices/limitsservice/LimitsServiceApplicationTests.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class LimitsServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /netflix-eureka-naming-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + netflix-eureka-naming-server + 0.0.1-SNAPSHOT + jar + + netflix-eureka-naming-server + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java + +```java +package com.in28minutes.microservices.netflixeurekanamingserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +@EnableEurekaServer +public class NetflixEurekaNamingServerApplication { + + public static void main(String[] args) { + SpringApplication.run(NetflixEurekaNamingServerApplication.class, args); + } +} +``` +--- + +### /netflix-eureka-naming-server/src/main/resources/application.properties + +```properties + +spring.application.name=netflix-eureka-naming-server +server.port=8761 + +eureka.client.register-with-eureka=false +eureka.client.fetch-registry=false +``` +--- + +### /netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java + +```java +package com.in28minutes.microservices.netflixeurekanamingserver; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class NetflixEurekaNamingServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /spring-cloud-config-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + spring-cloud-config-server + 0.0.1-SNAPSHOT + jar + + spring-cloud-config-server + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.cloud + spring-cloud-config-server + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /spring-cloud-config-server/src/main/java/com/in28minutes/microservices/springcloudconfigserver/SpringCloudConfigServerApplication.java + +```java +package com.in28minutes.microservices.springcloudconfigserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; + +@EnableConfigServer +@SpringBootApplication +public class SpringCloudConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudConfigServerApplication.class, args); + } +} +``` +--- + +### /spring-cloud-config-server/src/main/resources/application.properties + +```properties +spring.application.name=spring-cloud-config-server +server.port=8888 +spring.cloud.config.server.git.uri=file:///in28Minutes/git/spring-micro-services/03.microservices/git-localconfig-repo +``` +--- + +### /spring-cloud-config-server/src/test/java/com/in28minutes/microservices/springcloudconfigserver/SpringCloudConfigServerApplicationTests.java + +```java +package com.in28minutes.microservices.springcloudconfigserver; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringCloudConfigServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/03.microservices/step25.zip b/03.microservices/step25.zip new file mode 100644 index 0000000000000000000000000000000000000000..b95839ef437bcffa08f934818e89ba7441cfbbc4 GIT binary patch literal 39413 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 LXK&B}{n!5ii&tbE literal 0 HcmV?d00001 diff --git a/03.microservices/step29.md b/03.microservices/step29.md new file mode 100644 index 00000000..dd81d07f --- /dev/null +++ b/03.microservices/step29.md @@ -0,0 +1,1300 @@ + +## Complete Code Example + + +### /currency-conversion-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + currency-conversion-service + 0.0.1-SNAPSHOT + jar + + currency-conversion-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-feign + + + + org.springframework.cloud + spring-cloud-starter-eureka + + + + org.springframework.cloud + spring-cloud-starter-ribbon + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionBean.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import java.math.BigDecimal; + +public class CurrencyConversionBean { + private Long id; + private String from; + private String to; + private BigDecimal conversionMultiple; + private BigDecimal quantity; + private BigDecimal totalCalculatedAmount; + private int port; + + public CurrencyConversionBean() { + + } + + public CurrencyConversionBean(Long id, String from, String to, BigDecimal conversionMultiple, BigDecimal quantity, + BigDecimal totalCalculatedAmount, int port) { + super(); + this.id = id; + this.from = from; + this.to = to; + this.conversionMultiple = conversionMultiple; + this.quantity = quantity; + this.totalCalculatedAmount = totalCalculatedAmount; + this.port = port; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public BigDecimal getConversionMultiple() { + return conversionMultiple; + } + + public void setConversionMultiple(BigDecimal conversionMultiple) { + this.conversionMultiple = conversionMultiple; + } + + public BigDecimal getQuantity() { + return quantity; + } + + public void setQuantity(BigDecimal quantity) { + this.quantity = quantity; + } + + public BigDecimal getTotalCalculatedAmount() { + return totalCalculatedAmount; + } + + public void setTotalCalculatedAmount(BigDecimal totalCalculatedAmount) { + this.totalCalculatedAmount = totalCalculatedAmount; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class CurrencyConversionController { + + @Autowired + private CurrencyExchangeServiceProxy proxy; + + @GetMapping("/currency-converter/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrency(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + // Feign - Problem 1 + Map uriVariables = new HashMap<>(); + uriVariables.put("from", from); + uriVariables.put("to", to); + + ResponseEntity responseEntity = new RestTemplate().getForEntity( + "http://localhost:8000/currency-exchange/from/{from}/to/{to}", CurrencyConversionBean.class, + uriVariables); + + CurrencyConversionBean response = responseEntity.getBody(); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + + @GetMapping("/currency-converter-feign/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrencyFeign(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + CurrencyConversionBean response = proxy.retrieveExchangeValue(from, to); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.feign.EnableFeignClients; + +@SpringBootApplication +@EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") +@EnableDiscoveryClient +public class CurrencyConversionServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyConversionServiceApplication.class, args); + } +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +//@FeignClient(name="currency-exchange-service", url="localhost:8000") +@FeignClient(name="currency-exchange-service") +@RibbonClient(name="currency-exchange-service") +public interface CurrencyExchangeServiceProxy { + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public CurrencyConversionBean retrieveExchangeValue + (@PathVariable("from") String from, @PathVariable("to") String to); +} +``` +--- + +### /currency-conversion-service/src/main/resources/application.properties + +```properties +spring.application.name=currency-conversion-service +server.port=8100 +eureka.client.service-url.default-zone=http://localhost:8761/eureka +#currency-exchange-service.ribbon.listOfServers=http://localhost:8000,http://localhost:8001 +``` +--- + +### /currency-conversion-service/src/test/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplicationTests.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CurrencyConversionServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /currency-exchange-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + currency-exchange-service + 0.0.1-SNAPSHOT + jar + + currency-exchange-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeController.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CurrencyExchangeController { + + @Autowired + private Environment environment; + + @Autowired + private ExchangeValueRepository repository; + + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public ExchangeValue retrieveExchangeValue + (@PathVariable String from, @PathVariable String to){ + + ExchangeValue exchangeValue = + repository.findByFromAndTo(from, to); + + exchangeValue.setPort( + Integer.parseInt(environment.getProperty("local.server.port"))); + + return exchangeValue; + } +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class CurrencyExchangeServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyExchangeServiceApplication.class, args); + } +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/ExchangeValue.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import java.math.BigDecimal; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class ExchangeValue { + + @Id + private Long id; + + @Column(name="currency_from") + private String from; + + @Column(name="currency_to") + private String to; + + private BigDecimal conversionMultiple; + private int port; + + public ExchangeValue() { + + } + + + public ExchangeValue(Long id, String from, String to, BigDecimal conversionMultiple) { + super(); + this.id = id; + this.from = from; + this.to = to; + this.conversionMultiple = conversionMultiple; + } + + public Long getId() { + return id; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public BigDecimal getConversionMultiple() { + return conversionMultiple; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/ExchangeValueRepository.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ExchangeValueRepository extends + JpaRepository{ + ExchangeValue findByFromAndTo(String from, String to); +} +``` +--- + +### /currency-exchange-service/src/main/resources/application.properties + +```properties +spring.application.name=currency-exchange-service +server.port=8000 + +spring.jpa.show-sql=true +spring.h2.console.enabled=true + +eureka.client.service-url.default-zone=http://localhost:8761/eureka +``` +--- + +### /currency-exchange-service/src/main/resources/data.sql + +``` +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10001,'USD','INR',65,0); +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10002,'EUR','INR',75,0); +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10003,'AUD','INR',25,0); +``` +--- + +### /currency-exchange-service/src/test/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplicationTests.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CurrencyExchangeServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /git-localconfig-repo/limits-service-dev.properties + +```properties +limits-service.minimum=1 +``` +--- + +### /git-localconfig-repo/limits-service-qa.properties + +```properties +limits-service.minimum=2 +limits-service.maximum=222 +``` +--- + +### /git-localconfig-repo/limits-service.properties + +```properties +limits-service.minimum=8 +limits-service.maximum=888 +``` +--- + +### /limits-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + limits-service + 0.0.1-SNAPSHOT + jar + + limits-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/bean/LimitConfiguration.java + +```java +package com.in28minutes.microservices.limitsservice.bean; + +public class LimitConfiguration { + private int maximum; + private int minimum; + + protected LimitConfiguration() { + + } + + public LimitConfiguration(int maximum, int minimum) { + super(); + this.maximum = maximum; + this.minimum = minimum; + } + + public int getMaximum() { + return maximum; + } + + public int getMinimum() { + return minimum; + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/Configuration.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties("limits-service") +public class Configuration { + + private int minimum; + private int maximum; + + public void setMinimum(int minimum) { + this.minimum = minimum; + } + + public void setMaximum(int maximum) { + this.maximum = maximum; + } + + public int getMinimum() { + return minimum; + } + + public int getMaximum() { + return maximum; + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.microservices.limitsservice.bean.LimitConfiguration; + +@RestController +public class LimitsConfigurationController { + + @Autowired + private Configuration configuration; + + @GetMapping("/limits") + public LimitConfiguration retrieveLimitsFromConfigurations() { + return new LimitConfiguration(configuration.getMaximum(), + configuration.getMinimum()); + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsServiceApplication.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LimitsServiceApplication { + public static void main(String[] args) { + SpringApplication.run(LimitsServiceApplication.class, args); + } +} +``` +--- + +### /limits-service/src/main/resources/bootstrap.properties + +```properties +spring.application.name=limits-service +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=qa +``` +--- + +### /limits-service/src/test/java/com/in28minutes/microservices/limitsservice/LimitsServiceApplicationTests.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class LimitsServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /netflix-eureka-naming-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + netflix-eureka-naming-server + 0.0.1-SNAPSHOT + jar + + netflix-eureka-naming-server + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java + +```java +package com.in28minutes.microservices.netflixeurekanamingserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +@EnableEurekaServer +public class NetflixEurekaNamingServerApplication { + + public static void main(String[] args) { + SpringApplication.run(NetflixEurekaNamingServerApplication.class, args); + } +} +``` +--- + +### /netflix-eureka-naming-server/src/main/resources/application.properties + +```properties + +spring.application.name=netflix-eureka-naming-server +server.port=8761 + +eureka.client.register-with-eureka=false +eureka.client.fetch-registry=false +``` +--- + +### /netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java + +```java +package com.in28minutes.microservices.netflixeurekanamingserver; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class NetflixEurekaNamingServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /spring-cloud-config-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + spring-cloud-config-server + 0.0.1-SNAPSHOT + jar + + spring-cloud-config-server + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.cloud + spring-cloud-config-server + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /spring-cloud-config-server/src/main/java/com/in28minutes/microservices/springcloudconfigserver/SpringCloudConfigServerApplication.java + +```java +package com.in28minutes.microservices.springcloudconfigserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; + +@EnableConfigServer +@SpringBootApplication +public class SpringCloudConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudConfigServerApplication.class, args); + } +} +``` +--- + +### /spring-cloud-config-server/src/main/resources/application.properties + +```properties +spring.application.name=spring-cloud-config-server +server.port=8888 +spring.cloud.config.server.git.uri=file:///in28Minutes/git/spring-micro-services/03.microservices/git-localconfig-repo +``` +--- + +### /spring-cloud-config-server/src/test/java/com/in28minutes/microservices/springcloudconfigserver/SpringCloudConfigServerApplicationTests.java + +```java +package com.in28minutes.microservices.springcloudconfigserver; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringCloudConfigServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/03.microservices/step29.zip b/03.microservices/step29.zip new file mode 100644 index 0000000000000000000000000000000000000000..106054ad56617e7a9765af4a20fe0a5424fec098 GIT binary patch literal 39563 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