diff --git a/03.microservices/git-localconfig-repo b/03.microservices/git-localconfig-repo index b0670f28..d39e38f6 160000 --- a/03.microservices/git-localconfig-repo +++ b/03.microservices/git-localconfig-repo @@ -1 +1 @@ -Subproject commit b0670f28ce80ffc550a4e553b27f1e88f30005e4 +Subproject commit d39e38f6ac2764b187de9edefad6fd93b3ee15d6 diff --git a/03.microservices/limits-service/pom.xml b/03.microservices/limits-service/pom.xml index b47ab38b..addafd55 100644 --- a/03.microservices/limits-service/pom.xml +++ b/03.microservices/limits-service/pom.xml @@ -14,15 +14,15 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.M3 - + 1.5.2.RELEASE + UTF-8 UTF-8 1.8 - Finchley.M2 + Dalston.RC1 @@ -30,6 +30,11 @@ org.springframework.cloud spring-cloud-starter-config + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + org.springframework.boot spring-boot-starter-web diff --git a/03.microservices/limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java b/03.microservices/limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java index e2ef9bb0..3ff601bd 100644 --- a/03.microservices/limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java +++ b/03.microservices/limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java @@ -14,8 +14,8 @@ public class LimitsConfigurationController { @GetMapping("/limits") public LimitConfiguration retrieveLimitsFromConfigurations() { - return new LimitConfiguration(configuration.getMaximum(), + LimitConfiguration limitConfiguration = new LimitConfiguration(configuration.getMaximum(), configuration.getMinimum()); + return limitConfiguration; } - } diff --git a/03.microservices/limits-service/src/main/resources/bootstrap.properties b/03.microservices/limits-service/src/main/resources/bootstrap.properties index 614cc9b9..c1ff7971 100644 --- a/03.microservices/limits-service/src/main/resources/bootstrap.properties +++ b/03.microservices/limits-service/src/main/resources/bootstrap.properties @@ -1,3 +1,4 @@ spring.application.name=limits-service spring.cloud.config.uri=http://localhost:8888 -spring.profiles.active=qa \ No newline at end of file +spring.profiles.active=qa +management.security.enabled=false \ No newline at end of file diff --git a/03.microservices/spring-cloud-config-server/pom.xml b/03.microservices/spring-cloud-config-server/pom.xml index 89e966da..8f481759 100644 --- a/03.microservices/spring-cloud-config-server/pom.xml +++ b/03.microservices/spring-cloud-config-server/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.M3 + 1.5.2.RELEASE @@ -22,7 +22,7 @@ UTF-8 UTF-8 1.8 - Finchley.M2 + Dalston.RC1 @@ -31,6 +31,11 @@ spring-cloud-config-server + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + org.springframework.boot spring-boot-starter-test diff --git a/step42.md b/step42.md new file mode 100644 index 00000000..b14bfc8c --- /dev/null +++ b/step42.md @@ -0,0 +1,3565 @@ + +## Complete Code Example + + +### /01.framework-introductions/jpa-in-10-steps/notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + 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 + + + + + + +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/resources/application.properties + +```properties +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +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 JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/step-completed.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/take-step-backup.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/notes.txt + +``` +Goals +Enable building production ready applications quickly +Provide common non-functional features +- embedded servers +- metrics +- health checks +- externalized configuration + +What Spring Boot is NOT! +ZERO code generation +Neither an application server nor a web server + +Features +Quick Starter Projects with Auto Configuration + - Web + - JPA +Embedded Servers - Tomcat, Jetty or Undertow +Production-ready features + - metrics and health checks + - externalized configuration + + +http://localhost:8080/books => Few hardcoded books + +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.basics + springboot-in-10-steps + 0.0.1-SNAPSHOT + jar + + springboot-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + + + + + + + + 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 + + + + + + +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +public class Book { + long id; + String name; + String author; + + public Book(long id, String name, String author) { + super(); + this.id = id; + this.name = name; + this.author = author; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + @Override + public String toString() { + return String.format("Book [id=%s, name=%s, author=%s]", id, name, author); + } + +} +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class BooksController { + @GetMapping("/books") + public List getAllBooks() { + return Arrays.asList( + new Book(1l, "Mastering Spring 5.2", "Ranga Karanam")); + } +} +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +@SpringBootApplication +public class SpringbootIn10StepsApplication { + + public static void main(String[] args) { + ApplicationContext applicationContext = + SpringApplication.run(SpringbootIn10StepsApplication.class, args); + + for (String name : applicationContext.getBeanDefinitionNames()) { + System.out.println(name); + } + } +} +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/resources/application.properties + +```properties +#logging.level.org.springframework = DEBUG +management.security.enabled=false +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +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 SpringbootIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /02.restful-web-services/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.rest.webservices + restful-web-services + 0.0.1-SNAPSHOT + jar + + restful-web-services + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M2 + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + + org.springframework.boot + spring-boot-starter-hateoas + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + io.springfox + springfox-swagger2 + 2.4.0 + + + + io.springfox + springfox-swagger-ui + 2.4.0 + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + 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 + + + + + + +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/exception/CustomizedResponseEntityExceptionHandler.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.exception; + +import java.util.Date; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.in28minutes.rest.webservices.restfulwebservices.user.UserNotFoundException; + +@ControllerAdvice +@RestController +public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(Exception.class) + public final ResponseEntity handleAllExceptions(Exception ex, WebRequest request) { + ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), + request.getDescription(false)); + return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(UserNotFoundException.class) + public final ResponseEntity handleUserNotFoundException(UserNotFoundException ex, WebRequest request) { + ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), + request.getDescription(false)); + return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND); + } + + @Override + protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, + HttpHeaders headers, HttpStatus status, WebRequest request) { + ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), "Validation Failed", + ex.getBindingResult().toString()); + return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST); + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/exception/ExceptionResponse.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.exception; + +import java.util.Date; + +public class ExceptionResponse { + private Date timestamp; + private String message; + private String details; + + public ExceptionResponse(Date timestamp, String message, String details) { + super(); + this.timestamp = timestamp; + this.message = message; + this.details = details; + } + + public Date getTimestamp() { + return timestamp; + } + + public String getMessage() { + return message; + } + + public String getDetails() { + return details; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/filtering/FilteringController.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.filtering; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.http.converter.json.MappingJacksonValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; + +@RestController +public class FilteringController { + + // field1,field2 + @GetMapping("/filtering") + public MappingJacksonValue retrieveSomeBean() { + SomeBean someBean = new SomeBean("value1", "value2", "value3"); + + SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2"); + + FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter); + + MappingJacksonValue mapping = new MappingJacksonValue(someBean); + + mapping.setFilters(filters); + + return mapping; + } + + // field2, field3 + @GetMapping("/filtering-list") + public MappingJacksonValue retrieveListOfSomeBeans() { + List list = Arrays.asList(new SomeBean("value1", "value2", "value3"), + new SomeBean("value12", "value22", "value32")); + + SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field2", "field3"); + + FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter); + + MappingJacksonValue mapping = new MappingJacksonValue(list); + + mapping.setFilters(filters); + + return mapping; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/filtering/SomeBean.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.filtering; + +import com.fasterxml.jackson.annotation.JsonFilter; + +@JsonFilter("SomeBeanFilter") +public class SomeBean { + + private String field1; + + private String field2; + + private String field3; + + public SomeBean(String field1, String field2, String field3) { + super(); + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public String getField1() { + return field1; + } + + public void setField1(String field1) { + this.field1 = field1; + } + + public String getField2() { + return field2; + } + + public void setField2(String field2) { + this.field2 = field2; + } + + public String getField3() { + return field3; + } + + public void setField3(String field3) { + this.field3 = field3; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldBean.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.helloworld; + +public class HelloWorldBean { + + private String message; + + public HelloWorldBean(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return String.format("HelloWorldBean [message=%s]", message); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldController.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.helloworld; + +import java.util.Locale; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +//Controller +@RestController +public class HelloWorldController { + + @Autowired + private MessageSource messageSource; + + @GetMapping(path = "/hello-world") + public String helloWorld() { + return "Hello World"; + } + + @GetMapping(path = "/hello-world-bean") + public HelloWorldBean helloWorldBean() { + return new HelloWorldBean("Hello World"); + } + + ///hello-world/path-variable/in28minutes + @GetMapping(path = "/hello-world/path-variable/{name}") + public HelloWorldBean helloWorldPathVariable(@PathVariable String name) { + return new HelloWorldBean(String.format("Hello World, %s", name)); + } + + @GetMapping(path = "/hello-world-internationalized") + public String helloWorldInternationalized( + @RequestHeader(name="Accept-Language", required=false) Locale locale) { + return messageSource.getMessage("good.morning.message", null, locale); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices; + +import java.util.Locale; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +@SpringBootApplication +public class RestfulWebServicesApplication { + + public static void main(String[] args) { + SpringApplication.run(RestfulWebServicesApplication.class, args); + } + + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver localeResolver = new SessionLocaleResolver(); + localeResolver.setDefaultLocale(Locale.US); + return localeResolver; + } + + @Bean + public ResourceBundleMessageSource messageSource() { + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename("messages"); + return messageSource; + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/SwaggerConfig.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + public static final Contact DEFAULT_CONTACT = new Contact( + "Ranga Karanam", "http://www.in28minutes.com", "in28minutes@gmail.com"); + + public static final ApiInfo DEFAULT_API_INFO = new ApiInfo( + "Awesome API Title", "Awesome API Description", "1.0", + "urn:tos", DEFAULT_CONTACT, + "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0"); + + private static final Set DEFAULT_PRODUCES_AND_CONSUMES = + new HashSet(Arrays.asList("application/json", + "application/xml")); + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(DEFAULT_API_INFO) + .produces(DEFAULT_PRODUCES_AND_CONSUMES) + .consumes(DEFAULT_PRODUCES_AND_CONSUMES); + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/Post.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Entity +public class Post { + + @Id + @GeneratedValue + private Integer id; + private String description; + + @ManyToOne(fetch=FetchType.LAZY) + @JsonIgnore + private User user; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + @Override + public String toString() { + return String.format("Post [id=%s, description=%s]", id, description); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/PostRepository.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PostRepository extends JpaRepository{ + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/User.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import java.util.Date; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.validation.constraints.Past; +import javax.validation.constraints.Size; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(description="All details about the user.") +@Entity +public class User { + + @Id + @GeneratedValue + private Integer id; + + @Size(min=2, message="Name should have atleast 2 characters") + @ApiModelProperty(notes="Name should have atleast 2 characters") + private String name; + + @Past + @ApiModelProperty(notes="Birth date should be in the past") + private Date birthDate; + + @OneToMany(mappedBy="user") + private List posts; + + protected User() { + + } + + public User(Integer id, String name, Date birthDate) { + super(); + this.id = id; + this.name = name; + this.birthDate = birthDate; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; + } + + public List getPosts() { + return posts; + } + + public void setPosts(List posts) { + this.posts = posts; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, birthDate=%s]", id, name, birthDate); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserDaoService.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Component; + +@Component +public class UserDaoService { + + private static List users = new ArrayList<>(); + + private static int usersCount = 3; + + static { + users.add(new User(1, "Adam", new Date())); + users.add(new User(2, "Eve", new Date())); + users.add(new User(3, "Jack", new Date())); + } + + public List findAll() { + return users; + } + + public User save(User user) { + if (user.getId() == null) { + user.setId(++usersCount); + } + users.add(user); + return user; + } + + public User findOne(int id) { + for (User user : users) { + if (user.getId() == id) { + return user; + } + } + return null; + } + + public User deleteById(int id) { + Iterator iterator = users.iterator(); + while (iterator.hasNext()) { + User user = iterator.next(); + if (user.getId() == id) { + iterator.remove(); + return user; + } + } + return null; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserJPAResource.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +import java.net.URI; +import java.util.List; +import java.util.Optional; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.mvc.ControllerLinkBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +@RestController +public class UserJPAResource { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PostRepository postRepository; + + @GetMapping("/jpa/users") + public List retrieveAllUsers() { + return userRepository.findAll(); + } + + @GetMapping("/jpa/users/{id}") + public Resource retrieveUser(@PathVariable int id) { + Optional user = userRepository.findById(id); + + if (!user.isPresent()) + throw new UserNotFoundException("id-" + id); + + // "all-users", SERVER_PATH + "/users" + // retrieveAllUsers + Resource resource = new Resource(user.get()); + + ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()); + + resource.add(linkTo.withRel("all-users")); + + // HATEOAS + + return resource; + } + + @DeleteMapping("/jpa/users/{id}") + public void deleteUser(@PathVariable int id) { + userRepository.deleteById(id); + } + + // + // input - details of user + // output - CREATED & Return the created URI + + // HATEOAS + + @PostMapping("/jpa/users") + public ResponseEntity createUser(@Valid @RequestBody User user) { + User savedUser = userRepository.save(user); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(savedUser.getId()) + .toUri(); + + return ResponseEntity.created(location).build(); + + } + + @GetMapping("/jpa/users/{id}/posts") + public List retrieveAllUsers(@PathVariable int id) { + Optional userOptional = userRepository.findById(id); + + if(!userOptional.isPresent()) { + throw new UserNotFoundException("id-" + id); + } + + return userOptional.get().getPosts(); + } + + + @PostMapping("/jpa/users/{id}/posts") + public ResponseEntity createPost(@PathVariable int id, @RequestBody Post post) { + + Optional userOptional = userRepository.findById(id); + + if(!userOptional.isPresent()) { + throw new UserNotFoundException("id-" + id); + } + + User user = userOptional.get(); + + post.setUser(user); + + postRepository.save(post); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(post.getId()) + .toUri(); + + return ResponseEntity.created(location).build(); + + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserNotFoundException.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserRepository.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserResource.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +import java.net.URI; +import java.util.List; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.mvc.ControllerLinkBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +@RestController +public class UserResource { + + @Autowired + private UserDaoService service; + + @GetMapping("/users") + public List retrieveAllUsers() { + return service.findAll(); + } + + @GetMapping("/users/{id}") + public Resource retrieveUser(@PathVariable int id) { + User user = service.findOne(id); + + if(user==null) + throw new UserNotFoundException("id-"+ id); + + + //"all-users", SERVER_PATH + "/users" + //retrieveAllUsers + Resource resource = new Resource(user); + + ControllerLinkBuilder linkTo = + linkTo(methodOn(this.getClass()).retrieveAllUsers()); + + resource.add(linkTo.withRel("all-users")); + + //HATEOAS + + return resource; + } + + @DeleteMapping("/users/{id}") + public void deleteUser(@PathVariable int id) { + User user = service.deleteById(id); + + if(user==null) + throw new UserNotFoundException("id-"+ id); + } + + // + // input - details of user + // output - CREATED & Return the created URI + + //HATEOAS + + @PostMapping("/users") + public ResponseEntity createUser(@Valid @RequestBody User user) { + User savedUser = service.save(user); + // CREATED + // /user/{id} savedUser.getId() + + URI location = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(savedUser.getId()).toUri(); + + return ResponseEntity.created(location).build(); + + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/Name.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +public class Name { + private String firstName; + private String lastName; + + public Name() { + } + + public Name(String firstName, String lastName) { + super(); + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/PersonV1.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +public class PersonV1 { + private String name; + + public PersonV1() { + super(); + } + + public PersonV1(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/PersonV2.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +public class PersonV2 { + private Name name; + + public PersonV2() { + super(); + } + + public PersonV2(Name name) { + super(); + this.name = name; + } + + public Name getName() { + return name; + } + + public void setName(Name name) { + this.name = name; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/PersonVersioningController.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class PersonVersioningController { + + @GetMapping("v1/person") + public PersonV1 personV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping("v2/person") + public PersonV2 personV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + + @GetMapping(value = "/person/param", params = "version=1") + public PersonV1 paramV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping(value = "/person/param", params = "version=2") + public PersonV2 paramV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + + @GetMapping(value = "/person/header", headers = "X-API-VERSION=1") + public PersonV1 headerV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping(value = "/person/header", headers = "X-API-VERSION=2") + public PersonV2 headerV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + + @GetMapping(value = "/person/produces", produces = "application/vnd.company.app-v1+json") + public PersonV1 producesV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping(value = "/person/produces", produces = "application/vnd.company.app-v2+json") + public PersonV2 producesV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + +} +``` +--- + +### /02.restful-web-services/src/main/resources/application.properties + +```properties +logging.level.org.springframework = info +spring.jackson.serialization.write-dates-as-timestamps=false +management.security.enabled=false +security.basic.enabled=false +security.user.name=username +security.user.password=password +spring.jpa.show-sql=true +spring.h2.console.enabled=true +``` +--- + +### /02.restful-web-services/src/main/resources/data.sql + +``` +insert into user values(10001, sysdate(), 'AB'); +insert into user values(10002, sysdate(), 'Jill'); +insert into user values(10003, sysdate(), 'Jam'); +insert into post values(11001, 'My First Post', 10001); +insert into post values(11002, 'My Second Post', 10001); +``` +--- + +### /02.restful-web-services/src/main/resources/messages.properties + +```properties +good.morning.message=Good Morning +``` +--- + +### /02.restful-web-services/src/main/resources/messages_fr.properties + +```properties +good.morning.message=Bonjour +``` +--- + +### /02.restful-web-services/src/main/resources/messages_nl.properties + +```properties +good.morning.message=Goede Morgen +``` +--- + +### /02.restful-web-services/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices; + +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 RestfulWebServicesApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /03.microservices/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.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-feign + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + 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-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 + + + + + + +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @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); + + logger.info("{}", response); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + +} +``` +--- + +### /03.microservices/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; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") +@EnableDiscoveryClient +public class CurrencyConversionServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyConversionServiceApplication.class, args); + } + + @Bean + public AlwaysSampler defaultSampler() { + return new AlwaysSampler(); + } + +} +``` +--- + +### /03.microservices/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") +@FeignClient(name="netflix-zuul-api-gateway-server") +@RibbonClient(name="currency-exchange-service") +public interface CurrencyExchangeServiceProxy { + //@GetMapping("/currency-exchange/from/{from}/to/{to}") + @GetMapping("/currency-exchange-service/currency-exchange/from/{from}/to/{to}") + public CurrencyConversionBean retrieveExchangeValue + (@PathVariable("from") String from, @PathVariable("to") String to); +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/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.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + + + 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 + + + + + + +``` +--- + +### /03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeController.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @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"))); + + logger.info("{}", exchangeValue); + + return exchangeValue; + } +} +``` +--- + +### /03.microservices/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; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableDiscoveryClient +public class CurrencyExchangeServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyExchangeServiceApplication.class, args); + } + + @Bean + public AlwaysSampler defaultSampler(){ + return new AlwaysSampler(); + } + +} +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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); +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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); +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/git-localconfig-repo/limits-service-dev.properties + +```properties +limits-service.minimum=1 +``` +--- + +### /03.microservices/git-localconfig-repo/limits-service-qa.properties + +```properties +limits-service.minimum=2 +limits-service.maximum=222 +``` +--- + +### /03.microservices/git-localconfig-repo/limits-service.properties + +```properties +limits-service.minimum=8 +limits-service.maximum=888 +management.security.enabled=false +``` +--- + +### /03.microservices/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 + 1.5.2.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + Dalston.RC1 + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + org.springframework.boot + spring-boot-starter-web + + + + 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 + + + + + + +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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() { + LimitConfiguration limitConfiguration = new LimitConfiguration(configuration.getMaximum(), + configuration.getMinimum()); + return limitConfiguration; + } +} +``` +--- + +### /03.microservices/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); + } +} +``` +--- + +### /03.microservices/limits-service/src/main/resources/bootstrap.properties + +```properties +spring.application.name=limits-service +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=qa +management.security.enabled=false +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/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.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + 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 + + + + + + +``` +--- + +### /03.microservices/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); + } +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + netflix-zuul-api-gateway-server + 0.0.1-SNAPSHOT + jar + + netflix-zuul-api-gateway-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-starter-eureka + + + + org.springframework.cloud + spring-cloud-starter-zuul + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + + 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 + + + + + + +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/main/java/com/in28minutes/microservices/netflixzuulapigatewayserver/NetflixZuulApiGatewayServerApplication.java + +```java +package com.in28minutes.microservices.netflixzuulapigatewayserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; + +@EnableZuulProxy +@EnableDiscoveryClient +@SpringBootApplication +public class NetflixZuulApiGatewayServerApplication { + + public static void main(String[] args) { + SpringApplication.run(NetflixZuulApiGatewayServerApplication.class, args); + } + + @Bean + public AlwaysSampler defaultSampler(){ + return new AlwaysSampler(); + } +} +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/main/java/com/in28minutes/microservices/netflixzuulapigatewayserver/ZuulLoggingFilter.java + +```java +package com.in28minutes.microservices.netflixzuulapigatewayserver; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; + +@Component +public class ZuulLoggingFilter extends ZuulFilter{ + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() { + HttpServletRequest request = + RequestContext.getCurrentContext().getRequest(); + logger.info("request -> {} request uri -> {}", + request, request.getRequestURI()); + return null; + } + + @Override + public String filterType() { + return "pre"; + } + + @Override + public int filterOrder() { + return 1; + } +} +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/main/resources/application.properties + +```properties +spring.application.name=netflix-zuul-api-gateway-server +server.port=8765 +eureka.client.service-url.default-zone=http://localhost:8761/eureka +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/test/java/com/in28minutes/microservices/netflixzuulapigatewayserver/NetflixZuulApiGatewayServerApplicationTests.java + +```java +package com.in28minutes.microservices.netflixzuulapigatewayserver; + +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 NetflixZuulApiGatewayServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /03.microservices/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 + 1.5.2.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + Dalston.RC1 + + + + + org.springframework.cloud + spring-cloud-config-server + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + 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 + + + + + + +``` +--- + +### /03.microservices/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); + } +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + zipkin-distributed-tracing-server + 0.0.1-SNAPSHOT + jar + + zipkin-distributed-tracing-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-sleuth-zipkin-stream + + + org.springframework.cloud + spring-cloud-starter-stream-rabbit + + + + io.zipkin.java + zipkin-autoconfigure-ui + 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 + + + + + + +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/src/main/java/com/in28minutes/microservices/zipkindistributedtracingserver/ZipkinDistributedTracingServerApplication.java + +```java +package com.in28minutes.microservices.zipkindistributedtracingserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import zipkin.server.EnableZipkinServer; + +@EnableZipkinServer +@SpringBootApplication +public class ZipkinDistributedTracingServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ZipkinDistributedTracingServerApplication.class, args); + } +} +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/src/main/resources/application.properties + +```properties +spring.application.name=zipkin-distributed-tracing-server +server.port=9411 +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/src/test/java/com/in28minutes/microservices/zipkindistributedtracingserver/ZipkinDistributedTracingServerApplicationTests.java + +```java +package com.in28minutes.microservices.zipkindistributedtracingserver; + +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 ZipkinDistributedTracingServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/step42.zip b/step42.zip new file mode 100644 index 00000000..87867008 Binary files /dev/null and b/step42.zip differ diff --git a/step43.md b/step43.md new file mode 100644 index 00000000..b14bfc8c --- /dev/null +++ b/step43.md @@ -0,0 +1,3565 @@ + +## Complete Code Example + + +### /01.framework-introductions/jpa-in-10-steps/notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + 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 + + + + + + +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/main/resources/application.properties + +```properties +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +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 JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/step-completed.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; +``` +--- + +### /01.framework-introductions/jpa-in-10-steps/take-step-backup.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/notes.txt + +``` +Goals +Enable building production ready applications quickly +Provide common non-functional features +- embedded servers +- metrics +- health checks +- externalized configuration + +What Spring Boot is NOT! +ZERO code generation +Neither an application server nor a web server + +Features +Quick Starter Projects with Auto Configuration + - Web + - JPA +Embedded Servers - Tomcat, Jetty or Undertow +Production-ready features + - metrics and health checks + - externalized configuration + + +http://localhost:8080/books => Few hardcoded books + +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.basics + springboot-in-10-steps + 0.0.1-SNAPSHOT + jar + + springboot-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + + + + + + + + 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 + + + + + + +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +public class Book { + long id; + String name; + String author; + + public Book(long id, String name, String author) { + super(); + this.id = id; + this.name = name; + this.author = author; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + @Override + public String toString() { + return String.format("Book [id=%s, name=%s, author=%s]", id, name, author); + } + +} +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class BooksController { + @GetMapping("/books") + public List getAllBooks() { + return Arrays.asList( + new Book(1l, "Mastering Spring 5.2", "Ranga Karanam")); + } +} +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +@SpringBootApplication +public class SpringbootIn10StepsApplication { + + public static void main(String[] args) { + ApplicationContext applicationContext = + SpringApplication.run(SpringbootIn10StepsApplication.class, args); + + for (String name : applicationContext.getBeanDefinitionNames()) { + System.out.println(name); + } + } +} +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/main/resources/application.properties + +```properties +#logging.level.org.springframework = DEBUG +management.security.enabled=false +``` +--- + +### /01.framework-introductions/springboot-in-10-steps/src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +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 SpringbootIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /02.restful-web-services/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.rest.webservices + restful-web-services + 0.0.1-SNAPSHOT + jar + + restful-web-services + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M2 + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + + org.springframework.boot + spring-boot-starter-hateoas + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + io.springfox + springfox-swagger2 + 2.4.0 + + + + io.springfox + springfox-swagger-ui + 2.4.0 + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + 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 + + + + + + +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/exception/CustomizedResponseEntityExceptionHandler.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.exception; + +import java.util.Date; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.in28minutes.rest.webservices.restfulwebservices.user.UserNotFoundException; + +@ControllerAdvice +@RestController +public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(Exception.class) + public final ResponseEntity handleAllExceptions(Exception ex, WebRequest request) { + ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), + request.getDescription(false)); + return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(UserNotFoundException.class) + public final ResponseEntity handleUserNotFoundException(UserNotFoundException ex, WebRequest request) { + ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), + request.getDescription(false)); + return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND); + } + + @Override + protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, + HttpHeaders headers, HttpStatus status, WebRequest request) { + ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), "Validation Failed", + ex.getBindingResult().toString()); + return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST); + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/exception/ExceptionResponse.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.exception; + +import java.util.Date; + +public class ExceptionResponse { + private Date timestamp; + private String message; + private String details; + + public ExceptionResponse(Date timestamp, String message, String details) { + super(); + this.timestamp = timestamp; + this.message = message; + this.details = details; + } + + public Date getTimestamp() { + return timestamp; + } + + public String getMessage() { + return message; + } + + public String getDetails() { + return details; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/filtering/FilteringController.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.filtering; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.http.converter.json.MappingJacksonValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; + +@RestController +public class FilteringController { + + // field1,field2 + @GetMapping("/filtering") + public MappingJacksonValue retrieveSomeBean() { + SomeBean someBean = new SomeBean("value1", "value2", "value3"); + + SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2"); + + FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter); + + MappingJacksonValue mapping = new MappingJacksonValue(someBean); + + mapping.setFilters(filters); + + return mapping; + } + + // field2, field3 + @GetMapping("/filtering-list") + public MappingJacksonValue retrieveListOfSomeBeans() { + List list = Arrays.asList(new SomeBean("value1", "value2", "value3"), + new SomeBean("value12", "value22", "value32")); + + SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field2", "field3"); + + FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter); + + MappingJacksonValue mapping = new MappingJacksonValue(list); + + mapping.setFilters(filters); + + return mapping; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/filtering/SomeBean.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.filtering; + +import com.fasterxml.jackson.annotation.JsonFilter; + +@JsonFilter("SomeBeanFilter") +public class SomeBean { + + private String field1; + + private String field2; + + private String field3; + + public SomeBean(String field1, String field2, String field3) { + super(); + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public String getField1() { + return field1; + } + + public void setField1(String field1) { + this.field1 = field1; + } + + public String getField2() { + return field2; + } + + public void setField2(String field2) { + this.field2 = field2; + } + + public String getField3() { + return field3; + } + + public void setField3(String field3) { + this.field3 = field3; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldBean.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.helloworld; + +public class HelloWorldBean { + + private String message; + + public HelloWorldBean(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return String.format("HelloWorldBean [message=%s]", message); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldController.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.helloworld; + +import java.util.Locale; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +//Controller +@RestController +public class HelloWorldController { + + @Autowired + private MessageSource messageSource; + + @GetMapping(path = "/hello-world") + public String helloWorld() { + return "Hello World"; + } + + @GetMapping(path = "/hello-world-bean") + public HelloWorldBean helloWorldBean() { + return new HelloWorldBean("Hello World"); + } + + ///hello-world/path-variable/in28minutes + @GetMapping(path = "/hello-world/path-variable/{name}") + public HelloWorldBean helloWorldPathVariable(@PathVariable String name) { + return new HelloWorldBean(String.format("Hello World, %s", name)); + } + + @GetMapping(path = "/hello-world-internationalized") + public String helloWorldInternationalized( + @RequestHeader(name="Accept-Language", required=false) Locale locale) { + return messageSource.getMessage("good.morning.message", null, locale); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices; + +import java.util.Locale; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +@SpringBootApplication +public class RestfulWebServicesApplication { + + public static void main(String[] args) { + SpringApplication.run(RestfulWebServicesApplication.class, args); + } + + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver localeResolver = new SessionLocaleResolver(); + localeResolver.setDefaultLocale(Locale.US); + return localeResolver; + } + + @Bean + public ResourceBundleMessageSource messageSource() { + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename("messages"); + return messageSource; + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/SwaggerConfig.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + public static final Contact DEFAULT_CONTACT = new Contact( + "Ranga Karanam", "http://www.in28minutes.com", "in28minutes@gmail.com"); + + public static final ApiInfo DEFAULT_API_INFO = new ApiInfo( + "Awesome API Title", "Awesome API Description", "1.0", + "urn:tos", DEFAULT_CONTACT, + "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0"); + + private static final Set DEFAULT_PRODUCES_AND_CONSUMES = + new HashSet(Arrays.asList("application/json", + "application/xml")); + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(DEFAULT_API_INFO) + .produces(DEFAULT_PRODUCES_AND_CONSUMES) + .consumes(DEFAULT_PRODUCES_AND_CONSUMES); + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/Post.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Entity +public class Post { + + @Id + @GeneratedValue + private Integer id; + private String description; + + @ManyToOne(fetch=FetchType.LAZY) + @JsonIgnore + private User user; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + @Override + public String toString() { + return String.format("Post [id=%s, description=%s]", id, description); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/PostRepository.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PostRepository extends JpaRepository{ + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/User.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import java.util.Date; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.validation.constraints.Past; +import javax.validation.constraints.Size; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(description="All details about the user.") +@Entity +public class User { + + @Id + @GeneratedValue + private Integer id; + + @Size(min=2, message="Name should have atleast 2 characters") + @ApiModelProperty(notes="Name should have atleast 2 characters") + private String name; + + @Past + @ApiModelProperty(notes="Birth date should be in the past") + private Date birthDate; + + @OneToMany(mappedBy="user") + private List posts; + + protected User() { + + } + + public User(Integer id, String name, Date birthDate) { + super(); + this.id = id; + this.name = name; + this.birthDate = birthDate; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; + } + + public List getPosts() { + return posts; + } + + public void setPosts(List posts) { + this.posts = posts; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, birthDate=%s]", id, name, birthDate); + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserDaoService.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Component; + +@Component +public class UserDaoService { + + private static List users = new ArrayList<>(); + + private static int usersCount = 3; + + static { + users.add(new User(1, "Adam", new Date())); + users.add(new User(2, "Eve", new Date())); + users.add(new User(3, "Jack", new Date())); + } + + public List findAll() { + return users; + } + + public User save(User user) { + if (user.getId() == null) { + user.setId(++usersCount); + } + users.add(user); + return user; + } + + public User findOne(int id) { + for (User user : users) { + if (user.getId() == id) { + return user; + } + } + return null; + } + + public User deleteById(int id) { + Iterator iterator = users.iterator(); + while (iterator.hasNext()) { + User user = iterator.next(); + if (user.getId() == id) { + iterator.remove(); + return user; + } + } + return null; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserJPAResource.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +import java.net.URI; +import java.util.List; +import java.util.Optional; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.mvc.ControllerLinkBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +@RestController +public class UserJPAResource { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PostRepository postRepository; + + @GetMapping("/jpa/users") + public List retrieveAllUsers() { + return userRepository.findAll(); + } + + @GetMapping("/jpa/users/{id}") + public Resource retrieveUser(@PathVariable int id) { + Optional user = userRepository.findById(id); + + if (!user.isPresent()) + throw new UserNotFoundException("id-" + id); + + // "all-users", SERVER_PATH + "/users" + // retrieveAllUsers + Resource resource = new Resource(user.get()); + + ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()); + + resource.add(linkTo.withRel("all-users")); + + // HATEOAS + + return resource; + } + + @DeleteMapping("/jpa/users/{id}") + public void deleteUser(@PathVariable int id) { + userRepository.deleteById(id); + } + + // + // input - details of user + // output - CREATED & Return the created URI + + // HATEOAS + + @PostMapping("/jpa/users") + public ResponseEntity createUser(@Valid @RequestBody User user) { + User savedUser = userRepository.save(user); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(savedUser.getId()) + .toUri(); + + return ResponseEntity.created(location).build(); + + } + + @GetMapping("/jpa/users/{id}/posts") + public List retrieveAllUsers(@PathVariable int id) { + Optional userOptional = userRepository.findById(id); + + if(!userOptional.isPresent()) { + throw new UserNotFoundException("id-" + id); + } + + return userOptional.get().getPosts(); + } + + + @PostMapping("/jpa/users/{id}/posts") + public ResponseEntity createPost(@PathVariable int id, @RequestBody Post post) { + + Optional userOptional = userRepository.findById(id); + + if(!userOptional.isPresent()) { + throw new UserNotFoundException("id-" + id); + } + + User user = userOptional.get(); + + post.setUser(user); + + postRepository.save(post); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(post.getId()) + .toUri(); + + return ResponseEntity.created(location).build(); + + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserNotFoundException.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserRepository.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/user/UserResource.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.user; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +import java.net.URI; +import java.util.List; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.mvc.ControllerLinkBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +@RestController +public class UserResource { + + @Autowired + private UserDaoService service; + + @GetMapping("/users") + public List retrieveAllUsers() { + return service.findAll(); + } + + @GetMapping("/users/{id}") + public Resource retrieveUser(@PathVariable int id) { + User user = service.findOne(id); + + if(user==null) + throw new UserNotFoundException("id-"+ id); + + + //"all-users", SERVER_PATH + "/users" + //retrieveAllUsers + Resource resource = new Resource(user); + + ControllerLinkBuilder linkTo = + linkTo(methodOn(this.getClass()).retrieveAllUsers()); + + resource.add(linkTo.withRel("all-users")); + + //HATEOAS + + return resource; + } + + @DeleteMapping("/users/{id}") + public void deleteUser(@PathVariable int id) { + User user = service.deleteById(id); + + if(user==null) + throw new UserNotFoundException("id-"+ id); + } + + // + // input - details of user + // output - CREATED & Return the created URI + + //HATEOAS + + @PostMapping("/users") + public ResponseEntity createUser(@Valid @RequestBody User user) { + User savedUser = service.save(user); + // CREATED + // /user/{id} savedUser.getId() + + URI location = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(savedUser.getId()).toUri(); + + return ResponseEntity.created(location).build(); + + } +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/Name.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +public class Name { + private String firstName; + private String lastName; + + public Name() { + } + + public Name(String firstName, String lastName) { + super(); + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/PersonV1.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +public class PersonV1 { + private String name; + + public PersonV1() { + super(); + } + + public PersonV1(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/PersonV2.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +public class PersonV2 { + private Name name; + + public PersonV2() { + super(); + } + + public PersonV2(Name name) { + super(); + this.name = name; + } + + public Name getName() { + return name; + } + + public void setName(Name name) { + this.name = name; + } + +} +``` +--- + +### /02.restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/versioning/PersonVersioningController.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices.versioning; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class PersonVersioningController { + + @GetMapping("v1/person") + public PersonV1 personV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping("v2/person") + public PersonV2 personV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + + @GetMapping(value = "/person/param", params = "version=1") + public PersonV1 paramV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping(value = "/person/param", params = "version=2") + public PersonV2 paramV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + + @GetMapping(value = "/person/header", headers = "X-API-VERSION=1") + public PersonV1 headerV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping(value = "/person/header", headers = "X-API-VERSION=2") + public PersonV2 headerV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + + @GetMapping(value = "/person/produces", produces = "application/vnd.company.app-v1+json") + public PersonV1 producesV1() { + return new PersonV1("Bob Charlie"); + } + + @GetMapping(value = "/person/produces", produces = "application/vnd.company.app-v2+json") + public PersonV2 producesV2() { + return new PersonV2(new Name("Bob", "Charlie")); + } + +} +``` +--- + +### /02.restful-web-services/src/main/resources/application.properties + +```properties +logging.level.org.springframework = info +spring.jackson.serialization.write-dates-as-timestamps=false +management.security.enabled=false +security.basic.enabled=false +security.user.name=username +security.user.password=password +spring.jpa.show-sql=true +spring.h2.console.enabled=true +``` +--- + +### /02.restful-web-services/src/main/resources/data.sql + +``` +insert into user values(10001, sysdate(), 'AB'); +insert into user values(10002, sysdate(), 'Jill'); +insert into user values(10003, sysdate(), 'Jam'); +insert into post values(11001, 'My First Post', 10001); +insert into post values(11002, 'My Second Post', 10001); +``` +--- + +### /02.restful-web-services/src/main/resources/messages.properties + +```properties +good.morning.message=Good Morning +``` +--- + +### /02.restful-web-services/src/main/resources/messages_fr.properties + +```properties +good.morning.message=Bonjour +``` +--- + +### /02.restful-web-services/src/main/resources/messages_nl.properties + +```properties +good.morning.message=Goede Morgen +``` +--- + +### /02.restful-web-services/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java + +```java +package com.in28minutes.rest.webservices.restfulwebservices; + +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 RestfulWebServicesApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /03.microservices/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.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-feign + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + 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-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 + + + + + + +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @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); + + logger.info("{}", response); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + +} +``` +--- + +### /03.microservices/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; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") +@EnableDiscoveryClient +public class CurrencyConversionServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyConversionServiceApplication.class, args); + } + + @Bean + public AlwaysSampler defaultSampler() { + return new AlwaysSampler(); + } + +} +``` +--- + +### /03.microservices/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") +@FeignClient(name="netflix-zuul-api-gateway-server") +@RibbonClient(name="currency-exchange-service") +public interface CurrencyExchangeServiceProxy { + //@GetMapping("/currency-exchange/from/{from}/to/{to}") + @GetMapping("/currency-exchange-service/currency-exchange/from/{from}/to/{to}") + public CurrencyConversionBean retrieveExchangeValue + (@PathVariable("from") String from, @PathVariable("to") String to); +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/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.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + + + 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 + + + + + + +``` +--- + +### /03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeController.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @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"))); + + logger.info("{}", exchangeValue); + + return exchangeValue; + } +} +``` +--- + +### /03.microservices/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; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableDiscoveryClient +public class CurrencyExchangeServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyExchangeServiceApplication.class, args); + } + + @Bean + public AlwaysSampler defaultSampler(){ + return new AlwaysSampler(); + } + +} +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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); +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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); +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/git-localconfig-repo/limits-service-dev.properties + +```properties +limits-service.minimum=1 +``` +--- + +### /03.microservices/git-localconfig-repo/limits-service-qa.properties + +```properties +limits-service.minimum=2 +limits-service.maximum=222 +``` +--- + +### /03.microservices/git-localconfig-repo/limits-service.properties + +```properties +limits-service.minimum=8 +limits-service.maximum=888 +management.security.enabled=false +``` +--- + +### /03.microservices/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 + 1.5.2.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + Dalston.RC1 + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + org.springframework.boot + spring-boot-starter-web + + + + 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 + + + + + + +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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; + } + +} +``` +--- + +### /03.microservices/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() { + LimitConfiguration limitConfiguration = new LimitConfiguration(configuration.getMaximum(), + configuration.getMinimum()); + return limitConfiguration; + } +} +``` +--- + +### /03.microservices/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); + } +} +``` +--- + +### /03.microservices/limits-service/src/main/resources/bootstrap.properties + +```properties +spring.application.name=limits-service +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=qa +management.security.enabled=false +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/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.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + 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 + + + + + + +``` +--- + +### /03.microservices/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); + } +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + netflix-zuul-api-gateway-server + 0.0.1-SNAPSHOT + jar + + netflix-zuul-api-gateway-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-starter-eureka + + + + org.springframework.cloud + spring-cloud-starter-zuul + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + + 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 + + + + + + +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/main/java/com/in28minutes/microservices/netflixzuulapigatewayserver/NetflixZuulApiGatewayServerApplication.java + +```java +package com.in28minutes.microservices.netflixzuulapigatewayserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; + +@EnableZuulProxy +@EnableDiscoveryClient +@SpringBootApplication +public class NetflixZuulApiGatewayServerApplication { + + public static void main(String[] args) { + SpringApplication.run(NetflixZuulApiGatewayServerApplication.class, args); + } + + @Bean + public AlwaysSampler defaultSampler(){ + return new AlwaysSampler(); + } +} +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/main/java/com/in28minutes/microservices/netflixzuulapigatewayserver/ZuulLoggingFilter.java + +```java +package com.in28minutes.microservices.netflixzuulapigatewayserver; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; + +@Component +public class ZuulLoggingFilter extends ZuulFilter{ + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() { + HttpServletRequest request = + RequestContext.getCurrentContext().getRequest(); + logger.info("request -> {} request uri -> {}", + request, request.getRequestURI()); + return null; + } + + @Override + public String filterType() { + return "pre"; + } + + @Override + public int filterOrder() { + return 1; + } +} +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/main/resources/application.properties + +```properties +spring.application.name=netflix-zuul-api-gateway-server +server.port=8765 +eureka.client.service-url.default-zone=http://localhost:8761/eureka +``` +--- + +### /03.microservices/netflix-zuul-api-gateway-server/src/test/java/com/in28minutes/microservices/netflixzuulapigatewayserver/NetflixZuulApiGatewayServerApplicationTests.java + +```java +package com.in28minutes.microservices.netflixzuulapigatewayserver; + +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 NetflixZuulApiGatewayServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /03.microservices/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 + 1.5.2.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + Dalston.RC1 + + + + + org.springframework.cloud + spring-cloud-config-server + + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + + 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 + + + + + + +``` +--- + +### /03.microservices/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); + } +} +``` +--- + +### /03.microservices/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 +``` +--- + +### /03.microservices/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() { + } + +} +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + zipkin-distributed-tracing-server + 0.0.1-SNAPSHOT + jar + + zipkin-distributed-tracing-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-sleuth-zipkin-stream + + + org.springframework.cloud + spring-cloud-starter-stream-rabbit + + + + io.zipkin.java + zipkin-autoconfigure-ui + 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 + + + + + + +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/src/main/java/com/in28minutes/microservices/zipkindistributedtracingserver/ZipkinDistributedTracingServerApplication.java + +```java +package com.in28minutes.microservices.zipkindistributedtracingserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import zipkin.server.EnableZipkinServer; + +@EnableZipkinServer +@SpringBootApplication +public class ZipkinDistributedTracingServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ZipkinDistributedTracingServerApplication.class, args); + } +} +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/src/main/resources/application.properties + +```properties +spring.application.name=zipkin-distributed-tracing-server +server.port=9411 +``` +--- + +### /03.microservices/zipkin-distributed-tracing-server/src/test/java/com/in28minutes/microservices/zipkindistributedtracingserver/ZipkinDistributedTracingServerApplicationTests.java + +```java +package com.in28minutes.microservices.zipkindistributedtracingserver; + +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 ZipkinDistributedTracingServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/step43.zip b/step43.zip new file mode 100644 index 00000000..170c4820 Binary files /dev/null and b/step43.zip differ