├── .gitignore ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── springboot-api ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── api │ │ │ ├── ApiApplication.java │ │ │ ├── client │ │ │ ├── CommentFeignClient.java │ │ │ ├── PostFeignClient.java │ │ │ ├── PostFeignClientFallback.java │ │ │ └── UserMockClient.java │ │ │ ├── config │ │ │ ├── ApiLoggingFilter.java │ │ │ ├── ApiLoggingFilterConfig.java │ │ │ ├── FeignClientConfig.java │ │ │ └── OpenApiConfig.java │ │ │ ├── controller │ │ │ ├── CommentController.java │ │ │ ├── CustomErrorController.java │ │ │ ├── MockErrorController.java │ │ │ ├── PostController.java │ │ │ └── UserController.java │ │ │ ├── domain │ │ │ ├── Comment.java │ │ │ ├── Post.java │ │ │ └── User.java │ │ │ ├── exception │ │ │ ├── ApiError.java │ │ │ └── ApiExceptionHandler.java │ │ │ └── service │ │ │ ├── CommentService.java │ │ │ ├── CommentServiceImpl.java │ │ │ ├── PostService.java │ │ │ ├── PostServiceImpl.java │ │ │ ├── UserService.java │ │ │ └── UserServiceImpl.java │ └── resources │ │ ├── application.yml │ │ └── templates │ │ ├── error.html │ │ └── error │ │ └── 404.html │ └── test │ ├── java │ └── com │ │ └── example │ │ └── api │ │ ├── ApiApplicationTests.java │ │ ├── client │ │ └── PostFeignClientTest.java │ │ ├── controller │ │ ├── PostControllerTest.java │ │ └── UserControllerTest.java │ │ ├── domain │ │ ├── PostTestData.java │ │ └── UserTestData.java │ │ └── service │ │ ├── PostServiceTest.java │ │ └── UserServiceTest.java │ └── resources │ ├── application-test.yml │ └── stubs │ └── posts.json ├── springboot-assertion ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── assertion │ │ │ ├── MyClass.java │ │ │ ├── SpringBootAssertApplication.java │ │ │ ├── dao │ │ │ └── UserRepository.java │ │ │ ├── model │ │ │ ├── AdminUser.java │ │ │ ├── Person.java │ │ │ ├── Product.java │ │ │ └── User.java │ │ │ └── service │ │ │ ├── FooService.java │ │ │ └── UserService.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── assertion │ ├── AssertJAssertThatTests.java │ ├── HamcrestAssertThatTests.java │ ├── IsPrimeNumber.java │ ├── JunitAssertionTests.java │ └── MyClassTest.java ├── springboot-config ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── SpringBootDemoApplication.java │ │ │ ├── actuator │ │ │ ├── CustomEndpoint.java │ │ │ └── ReleaseNotesEndpoint.java │ │ │ ├── condition │ │ │ ├── AnotherCustomCondition.java │ │ │ ├── CombinedConditionsWithAllMatch.java │ │ │ ├── CombinedConditionsWithAnyMatch.java │ │ │ ├── CombinedConditionsWithNoneMatch.java │ │ │ ├── CustomCondition.java │ │ │ ├── CustomConditionAnnotation.java │ │ │ └── PredefinedConditions.java │ │ │ ├── config │ │ │ ├── CourseConfig.java │ │ │ ├── CustomJacksonConfig.java │ │ │ ├── CustomServletConfig.java │ │ │ ├── ExpressionConfig.java │ │ │ ├── FeignClientConfig.java │ │ │ ├── InlineConfig.java │ │ │ ├── OpenApiConfig.java │ │ │ └── PersonConfig.java │ │ │ ├── controller │ │ │ ├── ConditionalController.java │ │ │ ├── ConfigController.java │ │ │ └── PostController.java │ │ │ ├── model │ │ │ └── Post.java │ │ │ └── service │ │ │ ├── PostService.java │ │ │ └── PostServiceImpl.java │ └── resources │ │ ├── application.properties │ │ ├── application.yml │ │ └── banner.txt │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── config │ └── CourseConfigTest.java ├── springboot-email ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── email │ │ │ ├── EmailApplication.java │ │ │ ├── config │ │ │ └── ThymeleafTemplateConfig.java │ │ │ ├── model │ │ │ └── Email.java │ │ │ └── service │ │ │ ├── EmailSenderController.java │ │ │ └── EmailSenderService.java │ └── resources │ │ ├── application.yml │ │ └── templates │ │ └── welcome-email.html │ └── test │ └── java │ └── com │ └── example │ └── email │ ├── EmailApplicationTests.java │ └── service │ └── EmailSenderServiceTest.java ├── springboot-jpa ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── example.jpa │ │ ├── SpringBootJpaApplication.java │ │ ├── config │ │ └── PersistenceConfig.java │ │ ├── controller │ │ ├── PostController.java │ │ ├── UserController.java │ │ └── UserPostController.java │ │ ├── dao │ │ ├── entity │ │ │ ├── Post.java │ │ │ ├── User.java │ │ │ └── UserGroup.java │ │ ├── repository │ │ │ ├── UserGroupRepository.java │ │ │ └── UserRepository.java │ │ └── spec │ │ │ ├── BaseSpecification.java │ │ │ └── UserSpecification.java │ │ ├── model │ │ ├── mapper │ │ │ └── UserModelMapper.java │ │ ├── query │ │ │ └── UserQueryModel.java │ │ └── request │ │ │ └── UserRequestModel.java │ │ └── service │ │ ├── CryptoService.java │ │ ├── CryptoServiceImpl.java │ │ ├── UserPostService.java │ │ ├── UserPostServiceImpl.java │ │ ├── UserService.java │ │ └── UserServiceImpl.java │ └── resources │ ├── application.properties │ ├── application.yml │ └── banner.txt ├── springboot-kafka ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── kafka │ │ │ ├── multi │ │ │ ├── KafkaMultiApplication.java │ │ │ ├── config │ │ │ │ ├── KafkaCustomProperties.java │ │ │ │ ├── KafkaMultipleConsumerConfig.java │ │ │ │ └── KafkaMultipleProducerConfig.java │ │ │ └── service │ │ │ │ ├── KafkaConsumerService.java │ │ │ │ ├── KafkaFirstConsumerServiceImpl.java │ │ │ │ ├── KafkaFirstProducerService.java │ │ │ │ ├── KafkaProducerService.java │ │ │ │ ├── KafkaSecondConsumerServiceImpl.java │ │ │ │ └── KafkaSecondProducerService.java │ │ │ └── single │ │ │ ├── KafkaApplication.java │ │ │ ├── config │ │ │ ├── KafkaConsumerConfig.java │ │ │ └── KafkaProducerConfig.java │ │ │ ├── controller │ │ │ └── KafkaController.java │ │ │ └── service │ │ │ ├── CryptoService.java │ │ │ ├── CryptoServiceImpl.java │ │ │ ├── KafkaConsumerService.java │ │ │ ├── KafkaConsumerServiceImpl.java │ │ │ ├── KafkaProducerService.java │ │ │ └── KafkaProducerServiceImpl.java │ └── resources │ │ ├── app │ │ └── store │ │ │ ├── keystore.jks │ │ │ └── truststore.jks │ │ ├── application-local.yml │ │ ├── application-multi.yml │ │ ├── application-prod.yml │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── kafka │ ├── KafkaApplicationTests.java │ └── service │ ├── KafkaConsumerServiceTest.java │ └── KafkaProducerServiceTest.java ├── springboot-openfeign ├── .gitignore ├── README.md ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── openfeign │ │ │ ├── OpenFeignApplication.java │ │ │ ├── client │ │ │ ├── UserFeignClient.java │ │ │ └── UserFeignClientFallback.java │ │ │ ├── config │ │ │ ├── ApacheHttp5FeignSslClientConfig.java │ │ │ └── FeignClientConfig.java │ │ │ ├── controller │ │ │ └── UserController.java │ │ │ ├── model │ │ │ ├── ListUserResponse.java │ │ │ ├── SingleUserResponse.java │ │ │ ├── User.java │ │ │ └── UserRequest.java │ │ │ └── service │ │ │ └── UserService.java │ └── resources │ │ ├── KeyStore.jks │ │ ├── TrustStore.jks │ │ └── application.yml │ └── test │ ├── java │ └── com │ │ └── example │ │ └── openfeign │ │ ├── OpenFeignApplicationTests.java │ │ ├── client │ │ └── UserFeignClientTest.java │ │ ├── controller │ │ └── UserControllerTest.java │ │ ├── domain │ │ └── UserTestData.java │ │ └── service │ │ └── UserServiceTest.java │ └── resources │ ├── application-test.yml │ └── stubs │ └── user.json └── springboot-xml ├── .gitignore ├── README.md ├── build.gradle └── src ├── main ├── java │ └── com │ │ └── example │ │ └── xml │ │ ├── XmlApplication.java │ │ ├── adapter │ │ ├── DateAdapter.java │ │ ├── DateTimeAdapter.java │ │ ├── EnumValueDeserializer.java │ │ └── TimeAdapter.java │ │ ├── controller │ │ └── XmlController.java │ │ └── service │ │ └── XmlParserService.java └── resources │ ├── application.yml │ ├── jaxb │ └── bindings.xjb │ └── schema │ └── schema.xsd └── test ├── java └── com │ └── example │ └── xml │ ├── XmlApplicationTests.java │ ├── controller │ └── XmlControllerTest.java │ └── service │ └── XmlParserServiceTest.java └── resources └── data └── accountSummary.xml /.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle ### 2 | .gradle 3 | /build/ 4 | 5 | ### Spring Tool Suite ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBean 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | /out/ 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbnuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | 28 | ### VS Code ### 29 | .vscode/ 30 | 31 | ### Logs ### 32 | *.logs 33 | 34 | ### Mac ### 35 | *.DS_Store -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.example' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0' 14 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 15 | } 16 | 17 | test { 18 | useJUnitPlatform() 19 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'springboot-examples' 2 | 3 | include 'springboot-api' 4 | include 'springboot-config' 5 | include 'springboot-jpa' 6 | include 'springboot-kafka' 7 | include 'springboot-openfeign' 8 | include 'springboot-email' 9 | include 'springboot-xml' 10 | include 'springboot-assertion' 11 | -------------------------------------------------------------------------------- /springboot-api/.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle ### 2 | .gradle 3 | /build/ 4 | 5 | ### Maven ### 6 | /target/ 7 | /bin/ 8 | !.mvn/wrapper/maven-wrapper.jar 9 | 10 | ### Spring Tool Suite ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBean 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | /out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbnuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | 36 | ### Logs ### 37 | *.logs 38 | 39 | ### Mac ### 40 | *.DS_Store -------------------------------------------------------------------------------- /springboot-api/README.md: -------------------------------------------------------------------------------- 1 | # springboot-api 2 | 3 | ## Overview 4 | Build RESTFul API with Spring Boot 5 | 6 | ## API Example Details 7 | 1. **Build RESTFul APIs** using `@RestController` and use of short annotations `@GetMapping`, `@PostMapping`, `@PutMapping`, and `@DeleteMapping` 8 | 2. **Service Layer** development using `@Service` to provide business logic to `@RestController` 9 | 3. **Global API Exception Handler** using `ApiExceptionHandler` to customize the API response for errors 10 | 4. **API Documentation** using Spring OpenAPI and Swagger configuration using `OpenApiConfig` 11 | 5. **API Logging** using `ApiLoggingFilterConfig` to log API request and response 12 | 6. **Consume RESTFul APIs** provided by other services using `@FeignClient` 13 | 7. **Boilerplate Code** generation using `Lombok` annotations such as `@Data`, `@Value`, `@ToString`, `@Getters`, and `@Setters` 14 | 8. **Unit and Integration Tests** for Controller and Service Layer -------------------------------------------------------------------------------- /springboot-api/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | ext { 16 | set('springCloudVersion', "2020.0.3") 17 | } 18 | 19 | dependencies { 20 | implementation 'org.springframework.boot:spring-boot-starter-web' 21 | implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' 22 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.9.RELEASE' 23 | implementation 'org.springdoc:springdoc-openapi-ui:latest.release' 24 | implementation 'commons-io:commons-io:2.6' 25 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' 26 | 27 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 28 | testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner' 29 | 30 | compileOnly 'org.projectlombok:lombok:1.18.26' 31 | annotationProcessor 'org.projectlombok:lombok:1.18.26' 32 | } 33 | 34 | dependencyManagement { 35 | imports { 36 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 37 | } 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | 44 | tasks.named("bootJar") { 45 | archiveClassifier = 'exec' 46 | enabled = true 47 | } 48 | 49 | tasks.named("jar") { 50 | archiveClassifier = '' 51 | } -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/ApiApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.api; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.openfeign.EnableFeignClients; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.web.client.RestTemplate; 8 | 9 | @SpringBootApplication 10 | @EnableFeignClients 11 | public class ApiApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ApiApplication.class, args); 15 | } 16 | 17 | @Bean 18 | public RestTemplate getRestTemplate() { 19 | return new RestTemplate(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/client/CommentFeignClient.java: -------------------------------------------------------------------------------- 1 | package com.example.api.client; 2 | 3 | import com.example.api.domain.Comment; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import java.util.List; 8 | 9 | @FeignClient(name = "commentFeignClient", url = "https://jsonplaceholder.typicode.com/comments") 10 | public interface CommentFeignClient { 11 | 12 | @GetMapping 13 | List getAllComments(); 14 | 15 | @GetMapping("/{commentId}") 16 | Comment getCommentById(@PathVariable Long commentId); 17 | 18 | @GetMapping 19 | List getCommentsByPostId(@RequestParam Long postId); 20 | 21 | @PostMapping 22 | Comment createComment(Comment comment); 23 | 24 | @PutMapping 25 | Comment updateComment(Comment comment); 26 | 27 | @DeleteMapping("/{commentId}") 28 | Comment deleteComment(@PathVariable Long commentId); 29 | } 30 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/client/PostFeignClient.java: -------------------------------------------------------------------------------- 1 | package com.example.api.client; 2 | 3 | import com.example.api.config.FeignClientConfig; 4 | import com.example.api.domain.Post; 5 | import org.springframework.cloud.openfeign.FeignClient; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import java.util.List; 9 | 10 | @FeignClient(name = "postFeignClient", 11 | url = "${client.post.baseUrl}", 12 | configuration = FeignClientConfig.class, 13 | fallback = PostFeignClientFallback.class) 14 | public interface PostFeignClient { 15 | @GetMapping("/posts") 16 | List getAllPosts(); 17 | 18 | @GetMapping("/posts/{postId}") 19 | Post getPostById(@PathVariable Long postId); 20 | 21 | @GetMapping("/posts") 22 | List getPostByUserId(@RequestParam Long userId); 23 | 24 | @PostMapping("/posts") 25 | Post createPost(Post post); 26 | 27 | @PutMapping("/posts") 28 | Post updatePost(Post post); 29 | 30 | @DeleteMapping("/posts/{postId}") 31 | Post deletePost(@PathVariable Long postId); 32 | } 33 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/client/PostFeignClientFallback.java: -------------------------------------------------------------------------------- 1 | package com.example.api.client; 2 | 3 | import com.example.api.domain.Post; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | @Component 10 | public class PostFeignClientFallback implements PostFeignClient{ 11 | @Override 12 | public List getAllPosts() { 13 | return Collections.emptyList(); 14 | } 15 | 16 | @Override 17 | public Post getPostById(Long postId) { 18 | return null; 19 | } 20 | 21 | @Override 22 | public List getPostByUserId(Long userId) { 23 | return Collections.emptyList(); 24 | } 25 | 26 | @Override 27 | public Post createPost(Post post) { 28 | return null; 29 | } 30 | 31 | @Override 32 | public Post updatePost(Post post) { 33 | return null; 34 | } 35 | 36 | @Override 37 | public Post deletePost(Long postId) { 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/client/UserMockClient.java: -------------------------------------------------------------------------------- 1 | package com.example.api.client; 2 | 3 | import com.example.api.domain.User; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.time.LocalDate; 7 | import java.time.LocalDateTime; 8 | import java.time.ZonedDateTime; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Random; 12 | 13 | @Component 14 | public class UserMockClient { 15 | 16 | private Random random = new Random(); 17 | private List userList = new ArrayList<>( 18 | List.of(User.builder().id(1L).name("Adam").dateOfBirth(LocalDate.of(1950, 1, 1)).build(), 19 | User.builder().id(2L).name("Bob").dateOfBirth(LocalDate.of(1990, 10, 30)).build(), 20 | User.builder().id(3L).name("Charlie").dateOfBirth(LocalDate.of(1979, 7, 26)).build())); 21 | 22 | public List getAllUsers() { 23 | userList.forEach(user -> user.setLastLogin(LocalDateTime.now().minusDays(random.nextInt(10)))); 24 | return userList; 25 | } 26 | 27 | public User getUserById(Long id) { 28 | return userList.stream().filter(user -> user.getId().equals(id)).peek(user -> { 29 | user.setLastLogin(LocalDateTime.now()); 30 | user.setZonedDateTime(ZonedDateTime.now()); 31 | }).findAny().orElse(null); 32 | } 33 | 34 | public Long createUser(User user) { 35 | Long id = (long) (this.userList.size() + 1); 36 | user.setId(id); 37 | userList.add(user); 38 | return id; 39 | } 40 | 41 | public boolean updateUser(Long id, User user) { 42 | userList.forEach(item -> { 43 | if (item.getId().equals(id)) { 44 | item.setName(user.getName()); 45 | item.setDateOfBirth(user.getDateOfBirth()); 46 | } 47 | }); 48 | return true; 49 | } 50 | 51 | public boolean deleteUserById(Long id) { 52 | userList.removeIf(user -> user.getId().equals(id)); 53 | return true; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/config/ApiLoggingFilterConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.api.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 5 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @ConditionalOnExpression("${api.logging.enable:true}") 11 | public class ApiLoggingFilterConfig { 12 | 13 | @Value("${api.logging.url-patterns}") 14 | private String[] urlPatterns; 15 | 16 | @Value("${api.logging.requestIdParamName:requestId}") 17 | private String requestIdParamName; 18 | 19 | @Value("${api.logging.requestIdMDCParamName:REQUEST_ID}") 20 | private String requestIdMDCParamName; 21 | 22 | @Bean 23 | public FilterRegistrationBean loggingFilter() { 24 | FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); 25 | registrationBean.setFilter(new ApiLoggingFilter(requestIdParamName, requestIdMDCParamName)); 26 | registrationBean.addUrlPatterns(urlPatterns); 27 | return registrationBean; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/config/FeignClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.api.config; 2 | 3 | import feign.RequestInterceptor; 4 | import feign.auth.BasicAuthRequestInterceptor; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | public class FeignClientConfig { 8 | 9 | /** 10 | * Enable this bean if you want to add headers in HTTP request 11 | */ 12 | @Bean 13 | public RequestInterceptor requestInterceptor() { 14 | return requestTemplate -> { 15 | requestTemplate.header("Content-Type", "application/json"); 16 | requestTemplate.header("Accept", "application/json"); 17 | requestTemplate.header("header_1", "value_1"); 18 | requestTemplate.header("header_2", "value_2"); 19 | requestTemplate.header("header_3", "value_3"); 20 | }; 21 | } 22 | 23 | /** 24 | * Enable this bean if you want to add basic Authorization header 25 | * for e.g. Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= 26 | */ 27 | @Bean 28 | public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { 29 | return new BasicAuthRequestInterceptor("username", "password"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.api.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Contact; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @ConditionalOnProperty(name="springdoc.swagger-ui.enabled", havingValue = "true", matchIfMissing = true) 14 | public class OpenApiConfig { 15 | 16 | @Value("${api.info.title: api.info.title}") 17 | private String title; 18 | 19 | @Value("${api.info.description: api.info.description}") 20 | private String description; 21 | 22 | @Value("${api.info.version: api.info.version}") 23 | private String version; 24 | 25 | @Value("${api.info.term-of-service: api.info.terms-of-service}") 26 | private String termOfService; 27 | 28 | @Value("${api.info.contact.name: api.info.contact.name}") 29 | private String contactName; 30 | 31 | @Value("${api.info.contact.email: api.info.contact.email}") 32 | private String contactEmail; 33 | 34 | @Value("${api.info.contact.url: api.info.contact.url}") 35 | private String contactUrl; 36 | 37 | @Value("${api.info.license.name: api.info.license.name}") 38 | private String licenseName; 39 | 40 | @Value("${api.info.license.url: api.info.license.url}") 41 | private String licenseUrl; 42 | 43 | @Bean 44 | public OpenAPI api() { 45 | return new OpenAPI() 46 | .info(info()); 47 | } 48 | 49 | private Info info() { 50 | return new Info() 51 | .title(title) 52 | .description(description) 53 | .version(version) 54 | .contact(new Contact().name(contactName).email(contactEmail).url(contactUrl)) 55 | .license(new License().name(licenseName).url(licenseUrl)); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/controller/CommentController.java: -------------------------------------------------------------------------------- 1 | package com.example.api.controller; 2 | 3 | import com.example.api.domain.Comment; 4 | import com.example.api.service.CommentService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | 11 | @RestController 12 | @RequestMapping("/comments") 13 | @RequiredArgsConstructor 14 | public class CommentController { 15 | 16 | private final CommentService commentService; 17 | 18 | @GetMapping 19 | public List getAllComments() { 20 | return commentService.getAllComments(); 21 | } 22 | 23 | @GetMapping("/{commentId}") 24 | public Comment getCommentById(@PathVariable Long commentId) { 25 | return commentService.getCommentById(commentId); 26 | } 27 | 28 | @PostMapping 29 | @ResponseStatus(HttpStatus.CREATED) 30 | public Comment createComment(Comment comment) { 31 | return commentService.createComment(comment); 32 | } 33 | 34 | @PutMapping("/{commentId}") 35 | @ResponseStatus(HttpStatus.OK) 36 | public void updateComment(@PathVariable Long commentId, Comment comment) { 37 | commentService.updateComment(commentId, comment); 38 | } 39 | 40 | @DeleteMapping("/{commentId}") 41 | @ResponseStatus(HttpStatus.OK) 42 | public void deleteComment(@PathVariable Long commentId) { 43 | commentService.deleteComment(commentId); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/controller/CustomErrorController.java: -------------------------------------------------------------------------------- 1 | package com.example.api.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.web.error.ErrorAttributeOptions; 5 | import org.springframework.boot.web.servlet.error.ErrorAttributes; 6 | import org.springframework.boot.web.servlet.error.ErrorController; 7 | import org.springframework.cloud.util.ConditionalOnBootstrapDisabled; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.context.request.WebRequest; 11 | 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.util.Map; 14 | 15 | 16 | public class CustomErrorController implements ErrorController { 17 | private static final String PATH = "/error"; 18 | 19 | @Autowired 20 | private ErrorAttributes errorAttributes; 21 | 22 | @RequestMapping(PATH) 23 | public Map error(WebRequest request, HttpServletResponse response) { 24 | return getErrorAttributes(request, true); 25 | } 26 | 27 | private Map getErrorAttributes(WebRequest request, boolean includeStackTrace) { 28 | ErrorAttributeOptions options = ErrorAttributeOptions.defaults() 29 | .including(ErrorAttributeOptions.Include.MESSAGE) 30 | .including(ErrorAttributeOptions.Include.EXCEPTION) 31 | .including(ErrorAttributeOptions.Include.BINDING_ERRORS); 32 | if(includeStackTrace){ 33 | options = options.including(ErrorAttributeOptions.Include.STACK_TRACE); 34 | } 35 | return this.errorAttributes.getErrorAttributes(request, options); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/controller/MockErrorController.java: -------------------------------------------------------------------------------- 1 | package com.example.api.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | @RequestMapping("/mockerror") 10 | public class MockErrorController { 11 | 12 | @GetMapping("/5xx") 13 | public void throwInternalServerError(){ 14 | throw new RuntimeException("Mocked Runtime Exception"); 15 | } 16 | 17 | @PostMapping("/4xx") 18 | public void methodNotAllowed(){ 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/controller/PostController.java: -------------------------------------------------------------------------------- 1 | package com.example.api.controller; 2 | 3 | import com.example.api.domain.Comment; 4 | import com.example.api.domain.Post; 5 | import com.example.api.service.CommentService; 6 | import com.example.api.service.PostService; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import java.util.List; 12 | 13 | @RestController 14 | @RequestMapping("/posts") 15 | @RequiredArgsConstructor 16 | public class PostController { 17 | 18 | private final PostService postService; 19 | private final CommentService commentService; 20 | 21 | @GetMapping 22 | public List getAllPosts() { 23 | return postService.getAllPosts(); 24 | } 25 | 26 | @GetMapping("/{postId}") 27 | public Post getPostById(@PathVariable Long postId) { 28 | return postService.getPostById(postId); 29 | } 30 | 31 | @GetMapping("/{postId}/comments") 32 | public List getAllCommentsByPostId(@PathVariable Long postId) { 33 | return commentService.getAllCommentsByPostId(postId); 34 | } 35 | 36 | @PostMapping 37 | @ResponseStatus(HttpStatus.CREATED) 38 | public Post createPost(Post post) { 39 | return postService.createPost(post); 40 | } 41 | 42 | @PutMapping("/{postId}") 43 | @ResponseStatus(HttpStatus.OK) 44 | public void updatePost(@PathVariable Long postId, Post post) { 45 | postService.updatePost(postId, post); 46 | } 47 | 48 | @DeleteMapping("/{postId}") 49 | @ResponseStatus(HttpStatus.OK) 50 | public void deletePost(@PathVariable Long postId) { 51 | postService.deletePost(postId); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.api.controller; 2 | 3 | import com.example.api.domain.Post; 4 | import com.example.api.domain.User; 5 | import com.example.api.service.PostService; 6 | import com.example.api.service.UserService; 7 | import io.swagger.v3.oas.annotations.Operation; 8 | import io.swagger.v3.oas.annotations.media.ArraySchema; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/users") 18 | @RequiredArgsConstructor 19 | public class UserController { 20 | 21 | private final UserService userService; 22 | private final PostService postService; 23 | 24 | @Operation(summary = "Get All Users") 25 | @GetMapping 26 | public List getAllUsers() { 27 | return userService.getAllUsers(); 28 | } 29 | 30 | @Operation(summary = "Get User By Id") 31 | @GetMapping("/{userId}") 32 | public User getUserById(@PathVariable Long userId) { 33 | return userService.getUserById(userId); 34 | } 35 | 36 | @Operation(summary = "Get All Posts of Given User Id") 37 | @GetMapping("/{userId}/posts") 38 | public List getAllPostByUserId(@PathVariable Long userId) { 39 | return postService.getAllPostsByUserId(userId); 40 | } 41 | 42 | @Operation(summary = "Create New User") 43 | @PostMapping 44 | @ResponseStatus(HttpStatus.CREATED) 45 | public Long createUser(@RequestBody User user) { 46 | return userService.createUser(user); 47 | } 48 | 49 | @Operation(summary = "Update User") 50 | @PutMapping("/{userId}") 51 | @ResponseStatus(HttpStatus.OK) 52 | public void updateUser(@PathVariable Long userId, User user) { 53 | userService.updateUser(userId, user); 54 | } 55 | 56 | @Operation(summary = "Delete User") 57 | @DeleteMapping("/{userId}") 58 | @ResponseStatus(HttpStatus.OK) 59 | public void deleteUserById(@PathVariable Long userId) { 60 | userService.deleteUserById(userId); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/domain/Comment.java: -------------------------------------------------------------------------------- 1 | package com.example.api.domain; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class Comment { 7 | 8 | Long postId; 9 | Long id; 10 | String name; 11 | String email; 12 | String body; 13 | } 14 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/domain/Post.java: -------------------------------------------------------------------------------- 1 | package com.example.api.domain; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class Post { 7 | 8 | Long id; 9 | Long userId; 10 | String title; 11 | String body; 12 | } 13 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.example.api.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 7 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 8 | import com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer; 9 | import lombok.Builder; 10 | import lombok.Getter; 11 | import lombok.Setter; 12 | 13 | import java.time.LocalDate; 14 | import java.time.LocalDateTime; 15 | import java.time.ZonedDateTime; 16 | 17 | @Builder 18 | @Getter 19 | @Setter 20 | @JsonInclude(JsonInclude.Include.USE_DEFAULTS) 21 | public class User { 22 | 23 | User() { 24 | } 25 | 26 | User(Long id, String name, String password, LocalDate dateOfBirth, LocalDateTime lastLogin, ZonedDateTime zonedDateTime) { 27 | this.id = id; 28 | this.name = name; 29 | this.password = password; 30 | this.dateOfBirth = dateOfBirth; 31 | this.lastLogin = lastLogin; 32 | this.zonedDateTime = zonedDateTime; 33 | } 34 | 35 | Long id; 36 | 37 | String name; 38 | 39 | @JsonInclude(JsonInclude.Include.NON_NULL) 40 | String password; 41 | 42 | @JsonFormat(pattern = "dd MMM yyyy") 43 | @JsonSerialize(using = LocalDateSerializer.class) 44 | LocalDate dateOfBirth; 45 | 46 | @JsonFormat(pattern = "dd MMM yyyy hh:mm:ss") 47 | @JsonSerialize(using = LocalDateTimeSerializer.class) 48 | LocalDateTime lastLogin; 49 | 50 | @JsonFormat(pattern = "yyyy-MM-dd@HH:mm:ss.SSSXXX", locale = "en_SG", timezone = "Asia/Singapore") 51 | @JsonSerialize(using = ZonedDateTimeSerializer.class) 52 | ZonedDateTime zonedDateTime; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/exception/ApiError.java: -------------------------------------------------------------------------------- 1 | package com.example.api.exception; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Getter 10 | public class ApiError { 11 | 12 | private HttpStatus status; 13 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss") 14 | private LocalDateTime timestamp; 15 | private String message; 16 | private String debugMessage; 17 | 18 | private ApiError() { 19 | timestamp = LocalDateTime.now(); 20 | } 21 | 22 | ApiError(HttpStatus status) { 23 | this(); 24 | this.status = status; 25 | } 26 | 27 | ApiError(HttpStatus status, Throwable ex) { 28 | this(); 29 | this.status = status; 30 | this.message = "Unexpected error"; 31 | this.debugMessage = ex.getLocalizedMessage(); 32 | } 33 | 34 | ApiError(HttpStatus status, String message, Throwable ex) { 35 | this(); 36 | this.status = status; 37 | this.message = message; 38 | this.debugMessage = ex.getLocalizedMessage(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/exception/ApiExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.api.exception; 2 | 3 | import feign.FeignException; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.HttpRequestMethodNotSupportedException; 7 | import org.springframework.web.bind.annotation.ControllerAdvice; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.ResponseStatus; 10 | 11 | @ControllerAdvice 12 | public class ApiExceptionHandler { 13 | 14 | @ExceptionHandler(RuntimeException.class) 15 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 16 | public ResponseEntity onRuntimeException(RuntimeException ex) { 17 | ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", ex); 18 | return buildResponseEntity(apiError); 19 | } 20 | 21 | @ExceptionHandler(HttpRequestMethodNotSupportedException.class) 22 | @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) 23 | public ResponseEntity onMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) { 24 | ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, "Method Not Allowed", ex); 25 | return buildResponseEntity(apiError); 26 | } 27 | 28 | @ExceptionHandler(FeignException.class) 29 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 30 | public ResponseEntity onFeignException(FeignException ex) { 31 | ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", ex); 32 | return buildResponseEntity(apiError); 33 | } 34 | 35 | private ResponseEntity buildResponseEntity(ApiError apiError) { 36 | return ResponseEntity 37 | .status(apiError.getStatus()) 38 | .body(apiError); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import java.util.List; 4 | 5 | import com.example.api.domain.Comment; 6 | 7 | public interface CommentService { 8 | 9 | List getAllComments(); 10 | 11 | Comment getCommentById(Long id); 12 | 13 | List getAllCommentsByPostId(Long commentId); 14 | 15 | Comment createComment(Comment comment); 16 | 17 | void updateComment(Long commentId, Comment comment); 18 | 19 | void deleteComment(Long commentId); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/service/CommentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.client.CommentFeignClient; 4 | import com.example.api.domain.Comment; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class CommentServiceImpl implements CommentService { 13 | 14 | private final CommentFeignClient commentFeignClient; 15 | 16 | @Override 17 | public List getAllComments() { 18 | return commentFeignClient.getAllComments(); 19 | } 20 | 21 | @Override 22 | public Comment getCommentById(Long id) { 23 | return commentFeignClient.getCommentById(id); 24 | } 25 | 26 | @Override 27 | public List getAllCommentsByPostId(Long postId) { 28 | return commentFeignClient.getCommentsByPostId(postId); 29 | } 30 | 31 | @Override 32 | public Comment createComment(Comment comment) { 33 | return commentFeignClient.createComment(comment); 34 | } 35 | 36 | @Override 37 | public void updateComment(Long commentId, Comment comment) { 38 | commentFeignClient.updateComment(comment); 39 | } 40 | 41 | @Override 42 | public void deleteComment(Long commentId) { 43 | commentFeignClient.deleteComment(commentId); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/service/PostService.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.domain.Post; 4 | 5 | import java.util.List; 6 | 7 | public interface PostService { 8 | 9 | List getAllPosts(); 10 | 11 | Post getPostById(Long postId); 12 | 13 | List getAllPostsByUserId(Long userId); 14 | 15 | Post createPost(Post post); 16 | 17 | void updatePost(Long postId, Post post); 18 | 19 | void deletePost(Long postId); 20 | } 21 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/service/PostServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.client.PostFeignClient; 4 | import com.example.api.domain.Post; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class PostServiceImpl implements PostService { 13 | 14 | private final PostFeignClient postFeignClient; 15 | 16 | @Override 17 | public List getAllPosts() { 18 | return postFeignClient.getAllPosts(); 19 | } 20 | 21 | @Override 22 | public Post getPostById(Long postId) { 23 | return postFeignClient.getPostById(postId); 24 | } 25 | 26 | @Override 27 | public List getAllPostsByUserId(Long userId) { 28 | return postFeignClient.getPostByUserId(userId); 29 | } 30 | 31 | @Override 32 | public Post createPost(Post post) { 33 | return postFeignClient.createPost(post); 34 | } 35 | 36 | @Override 37 | public void updatePost(Long postId, Post post) { 38 | postFeignClient.updatePost(post); 39 | } 40 | 41 | @Override 42 | public void deletePost(Long postId) { 43 | postFeignClient.deletePost(postId); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.domain.User; 4 | 5 | import java.util.List; 6 | 7 | public interface UserService { 8 | 9 | List getAllUsers(); 10 | 11 | User getUserById(Long id); 12 | 13 | Long createUser(User user); 14 | 15 | boolean updateUser(Long id, User user); 16 | 17 | boolean deleteUserById(Long id); 18 | } 19 | -------------------------------------------------------------------------------- /springboot-api/src/main/java/com/example/api/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.client.UserMockClient; 4 | import com.example.api.domain.User; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class UserServiceImpl implements UserService { 13 | 14 | private final UserMockClient userMockClient; 15 | 16 | @Override 17 | public List getAllUsers() { 18 | return userMockClient.getAllUsers(); 19 | } 20 | 21 | @Override 22 | public User getUserById(Long id) { 23 | return userMockClient.getUserById(id); 24 | } 25 | 26 | @Override 27 | public Long createUser(User user) { 28 | return userMockClient.createUser(user); 29 | } 30 | 31 | @Override 32 | public boolean updateUser(Long id, User user) { 33 | return userMockClient.updateUser(id, user); 34 | } 35 | 36 | @Override 37 | public boolean deleteUserById(Long id) { 38 | return userMockClient.deleteUserById(id); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /springboot-api/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #APP SPECIFIC CUSTOM PROPERTIES 2 | api: 3 | info: 4 | title: Spring Boot APIs 5 | description: Spring Boot API Examples 6 | version: 1.0.0 7 | terms-of-service: http://example.com/terms/ 8 | contact: 9 | name: Example API Team 10 | email: apiteam@example.com 11 | url: http://example.com/team 12 | license: 13 | name: Apache 2.0 14 | url: http://www.apache.org/licenses/LICENSE-2.0.html 15 | logging: 16 | enable: true 17 | url-patterns: "/users/*,/posts/*" 18 | requestIdParamName: CORRELATION_ID 19 | 20 | springdoc: 21 | swagger-ui: 22 | enabled: true 23 | path: / # Redirect localhost:8080 to swagger-ui.html 24 | 25 | server: 26 | port: 8080 27 | error: 28 | whitelabel: 29 | enabled: true 30 | 31 | logging: 32 | level: 33 | root: INFO 34 | com.example.api.client: DEBUG 35 | pattern: 36 | #console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" 37 | console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%8.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %X{REQUEST_ID} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" 38 | 39 | feign: 40 | circuitbreaker: 41 | enabled: true 42 | client: 43 | config: 44 | default: 45 | connectTimeout: 5000 46 | readTimeout: 5000 47 | loggerLevel: BASIC 48 | postFeignClient: 49 | loggerLevel: FULL 50 | 51 | client: 52 | post: 53 | baseUrl: https://jsonplaceholder.typicode.com 54 | 55 | spring: 56 | jackson: 57 | date-format: "dd-MM-yyyy hh:mm:ss" 58 | default-property-inclusion: use_defaults 59 | serialization: 60 | INDENT_OUTPUT: true 61 | FAIL_ON_EMPTY_BEANS: false 62 | WRITE_DATES_AS_TIMESTAMPS: false 63 | deserialization: 64 | FAIL_ON_UNKNOWN_PROPERTIES: false 65 | FAIL_ON_IGNORED_PROPERTIES: false 66 | -------------------------------------------------------------------------------- /springboot-api/src/main/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Something went wrong! 5 | 6 | 7 |

Generic Error - Status

8 |

Sorry for the inconvenience. Please contact the administrator.

9 |
10 |
    11 |
  • Timestamp: Timestamp
  • 12 |
  • Path: Path
  • 13 |
  • Error: Error
  • 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /springboot-api/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 404 - resource not found 10 | 11 | 12 |
13 |

Resource Not Found Error - 404

14 | 15 |

Requested resource was not found. Please contact the administrator.

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
TimestampTimestamp
PathPath
StatusStatus
ErrorError
MessageMessage
38 |
39 | 40 | -------------------------------------------------------------------------------- /springboot-api/src/test/java/com/example/api/ApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.api; 2 | 3 | import com.example.api.controller.CommentController; 4 | import com.example.api.controller.PostController; 5 | import com.example.api.controller.UserController; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | @SpringBootTest 13 | class ApiApplicationTests { 14 | 15 | @Autowired 16 | private UserController userController; 17 | 18 | @Autowired 19 | private PostController postController; 20 | 21 | @Autowired 22 | private CommentController commentController; 23 | 24 | @Test 25 | void contextLoads() { 26 | assertThat(userController).isNotNull(); 27 | assertThat(postController).isNotNull(); 28 | assertThat(commentController).isNotNull(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /springboot-api/src/test/java/com/example/api/client/PostFeignClientTest.java: -------------------------------------------------------------------------------- 1 | package com.example.api.client; 2 | 3 | import com.example.api.domain.Post; 4 | import org.apache.commons.io.IOUtils; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; 9 | import org.springframework.core.io.ClassPathResource; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.test.context.ActiveProfiles; 13 | 14 | import java.io.IOException; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.List; 17 | 18 | import static com.github.tomakehurst.wiremock.client.WireMock.*; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | @ActiveProfiles("test") 22 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 23 | @AutoConfigureWireMock(port = 9091) 24 | public class PostFeignClientTest { 25 | 26 | @Autowired 27 | PostFeignClient postFeignClient; 28 | 29 | @Test 30 | public void getAllPosts_whenValidClient_returnValidResponse() throws Exception { 31 | // Using WireMock to mock client API: 32 | stubFor(get(urlEqualTo("/posts")) 33 | .willReturn(aResponse() 34 | .withStatus(HttpStatus.OK.value()) 35 | .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) 36 | .withBody(read("stubs/posts.json")))); 37 | 38 | List posts = postFeignClient.getAllPosts(); 39 | Post post = posts.get(0); 40 | 41 | // We're asserting if WireMock responded properly 42 | assertThat(posts).hasSize(10); 43 | assertThat(post.getId()).isEqualTo(1); 44 | assertThat(post.getUserId()).isEqualTo(1); 45 | assertThat(post.getTitle()).isEqualTo("title"); 46 | assertThat(post.getBody()).isEqualTo("body"); 47 | } 48 | 49 | private String read(String location) throws IOException { 50 | return IOUtils.toString(new ClassPathResource(location).getInputStream(), StandardCharsets.UTF_8); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /springboot-api/src/test/java/com/example/api/domain/PostTestData.java: -------------------------------------------------------------------------------- 1 | package com.example.api.domain; 2 | 3 | import java.util.List; 4 | 5 | public class PostTestData { 6 | 7 | public static Post post(){ 8 | return new Post(1L ,1L, "title", "body"); 9 | } 10 | 11 | public static List posts() { 12 | return List.of(post()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /springboot-api/src/test/java/com/example/api/domain/UserTestData.java: -------------------------------------------------------------------------------- 1 | package com.example.api.domain; 2 | 3 | import java.time.LocalDate; 4 | import java.util.List; 5 | 6 | public class UserTestData { 7 | 8 | public static User user(){ 9 | return User.builder() 10 | .id(1L) 11 | .name( "Adam") 12 | .dateOfBirth(LocalDate.of(1986, 8, 22)) 13 | .build(); 14 | } 15 | 16 | public static List users() { 17 | return List.of(user()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springboot-api/src/test/java/com/example/api/service/PostServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.client.PostFeignClient; 4 | import com.example.api.domain.Post; 5 | import com.example.api.domain.PostTestData; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.mock.mockito.MockBean; 10 | 11 | import java.util.List; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | import static org.mockito.ArgumentMatchers.anyLong; 15 | import static org.mockito.Mockito.when; 16 | 17 | @SpringBootTest 18 | public class PostServiceTest { 19 | 20 | @MockBean 21 | private PostFeignClient postFeignClient; 22 | 23 | @Autowired 24 | private PostService postService; 25 | 26 | @Test 27 | public void getAllPosts_whenValidClientResponse_returnAllPosts() { 28 | when(postFeignClient.getAllPosts()).thenReturn(PostTestData.posts()); 29 | 30 | List posts = postService.getAllPosts(); 31 | 32 | assertThat(posts.size()).isEqualTo(1); 33 | assertThat(posts.get(0).getId()).isEqualTo(1); 34 | assertThat(posts.get(0).getUserId()).isEqualTo(1); 35 | assertThat(posts.get(0).getTitle()).isEqualTo("title"); 36 | assertThat(posts.get(0).getBody()).isEqualTo("body"); 37 | } 38 | 39 | 40 | @Test 41 | public void getPostById_whenValidPostId_returnThatPost() { 42 | when(postFeignClient.getPostById(anyLong())).thenReturn(PostTestData.post()); 43 | 44 | Post post = postService.getPostById(1L); 45 | 46 | assertThat(post.getId()).isEqualTo(1); 47 | assertThat(post.getUserId()).isEqualTo(1); 48 | assertThat(post.getTitle()).isEqualTo("title"); 49 | assertThat(post.getBody()).isEqualTo("body"); 50 | } 51 | 52 | @Test 53 | public void getAllPostsByUserId_whenValidUserId_returnAllPostsOfThatUser() { 54 | when(postFeignClient.getPostByUserId(anyLong())).thenReturn(PostTestData.posts()); 55 | 56 | List posts = postService.getAllPostsByUserId(1L); 57 | 58 | assertThat(posts.size()).isEqualTo(1); 59 | assertThat(posts.get(0).getId()).isEqualTo(1); 60 | assertThat(posts.get(0).getUserId()).isEqualTo(1); 61 | assertThat(posts.get(0).getTitle()).isEqualTo("title"); 62 | assertThat(posts.get(0).getBody()).isEqualTo("body"); 63 | } 64 | 65 | @Test 66 | public void createPost_whenValidPostData_createAndReturnThatPost() { 67 | when(postFeignClient.createPost(PostTestData.post())).thenReturn(PostTestData.post()); 68 | 69 | Post post = postService.createPost(PostTestData.post()); 70 | 71 | assertThat(post.getId()).isEqualTo(1); 72 | assertThat(post.getUserId()).isEqualTo(1); 73 | assertThat(post.getTitle()).isEqualTo("title"); 74 | assertThat(post.getBody()).isEqualTo("body"); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /springboot-api/src/test/java/com/example/api/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.api.service; 2 | 3 | import com.example.api.client.UserMockClient; 4 | import com.example.api.domain.User; 5 | import com.example.api.domain.UserTestData; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.mock.mockito.MockBean; 10 | 11 | import java.util.List; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | import static org.mockito.ArgumentMatchers.any; 15 | import static org.mockito.ArgumentMatchers.anyLong; 16 | import static org.mockito.Mockito.when; 17 | 18 | @SpringBootTest 19 | public class UserServiceTest { 20 | 21 | @MockBean 22 | private UserMockClient userMockClient; 23 | 24 | @Autowired 25 | private UserService userService; 26 | 27 | @Test 28 | public void getAllUsers_whenValidProviderResponse_returnAllUsers() { 29 | when(userMockClient.getAllUsers()).thenReturn(UserTestData.users()); 30 | 31 | List users = userService.getAllUsers(); 32 | 33 | assertThat(users.size()).isEqualTo(1); 34 | assertThat(users.get(0).getId()).isEqualTo(1); 35 | assertThat(users.get(0).getName()).isEqualTo("Adam"); 36 | assertThat(users.get(0).getDateOfBirth().toString()).isEqualTo("1986-08-22"); 37 | } 38 | 39 | @Test 40 | public void getUserById_whenValidUserId_returnThatUser() { 41 | when(userMockClient.getUserById(anyLong())).thenReturn(UserTestData.user()); 42 | 43 | User user = userService.getUserById(1L); 44 | 45 | assertThat(user.getId()).isEqualTo(1); 46 | assertThat(user.getName()).isEqualTo("Adam"); 47 | assertThat(user.getDateOfBirth().toString()).isEqualTo("1986-08-22"); 48 | } 49 | 50 | @Test 51 | public void createUser_whenValidUserData_createAndReturnUserId() { 52 | when(userMockClient.createUser(any(User.class))).thenReturn(UserTestData.user().getId()); 53 | 54 | Long id = userService.createUser(UserTestData.user()); 55 | 56 | assertThat(id).isEqualTo(1L); 57 | } 58 | 59 | @Test 60 | public void updateUser_whenValidUserData_updateAndReturnStatus() { 61 | when(userMockClient.updateUser(anyLong(), any(User.class))).thenReturn(true); 62 | 63 | Boolean status = userService.updateUser(UserTestData.user().getId(), UserTestData.user()); 64 | 65 | assertThat(status).isTrue(); 66 | } 67 | 68 | @Test 69 | public void deleteUser_whenValidUserId_deleteAndReturnStatus() { 70 | when(userMockClient.deleteUserById(anyLong())).thenReturn(true); 71 | 72 | Boolean status = userService.deleteUserById(UserTestData.user().getId()); 73 | 74 | assertThat(status).isTrue(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /springboot-api/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | client: 2 | post: 3 | baseUrl: http://localhost:9091 -------------------------------------------------------------------------------- /springboot-api/src/test/resources/stubs/posts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "userId": 1, 4 | "id": 1, 5 | "title": "title", 6 | "body": "body" 7 | }, 8 | { 9 | "userId": 1, 10 | "id": 2, 11 | "title": "qui est esse", 12 | "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" 13 | }, 14 | { 15 | "userId": 1, 16 | "id": 3, 17 | "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", 18 | "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut" 19 | }, 20 | { 21 | "userId": 1, 22 | "id": 4, 23 | "title": "eum et est occaecati", 24 | "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit" 25 | }, 26 | { 27 | "userId": 1, 28 | "id": 5, 29 | "title": "nesciunt quas odio", 30 | "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque" 31 | }, 32 | { 33 | "userId": 2, 34 | "id": 6, 35 | "title": "et ea vero quia laudantium autem", 36 | "body": "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi" 37 | }, 38 | { 39 | "userId": 2, 40 | "id": 7, 41 | "title": "in quibusdam tempore odit est dolorem", 42 | "body": "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio" 43 | }, 44 | { 45 | "userId": 2, 46 | "id": 8, 47 | "title": "dolorum ut in voluptas mollitia et saepe quo animi", 48 | "body": "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam" 49 | }, 50 | { 51 | "userId": 2, 52 | "id": 9, 53 | "title": "voluptatem eligendi optio", 54 | "body": "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum" 55 | }, 56 | { 57 | "userId": 2, 58 | "id": 10, 59 | "title": "eveniet quod temporibus", 60 | "body": "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae" 61 | } 62 | ] -------------------------------------------------------------------------------- /springboot-assertion/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /bin/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | /README.markdown 29 | -------------------------------------------------------------------------------- /springboot-assertion/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Assertion 2 | This spring boot project is to demonstrate writing test cases with different assertion libraries:- 3 | 4 | 1. JUnit 5 5 | 2. AssertJ 6 | 3. Hamcrest 7 | 8 | Check out the `test` package for various test case examples. -------------------------------------------------------------------------------- /springboot-assertion/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '2.7.6' 4 | id 'io.spring.dependency-management' version '1.0.15.RELEASE' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-web' 23 | compileOnly 'org.projectlombok:lombok' 24 | annotationProcessor 'org.projectlombok:lombok' 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | testImplementation 'org.hamcrest:hamcrest:2.2' 27 | testImplementation("org.assertj:assertj-core:3.23.1") 28 | } 29 | 30 | tasks.named('test') { 31 | useJUnitPlatform() 32 | } -------------------------------------------------------------------------------- /springboot-assertion/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.6 9 | 10 | 11 | com.example 12 | assertion 13 | 0.0.1-SNAPSHOT 14 | assertion 15 | Assertion project for Spring Boot 16 | 17 | 11 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | org.projectlombok 27 | lombok 28 | true 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | org.hamcrest 38 | hamcrest 39 | 2.2 40 | test 41 | 42 | 43 | org.assertj 44 | assertj-core 45 | 46 | 3.23.1 47 | test 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-maven-plugin 56 | 57 | 58 | 59 | org.projectlombok 60 | lombok 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/MyClass.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion; 2 | 3 | public class MyClass { 4 | 5 | public static int add(int a, int b){ 6 | return a+b; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/SpringBootAssertApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootAssertApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootAssertApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/dao/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.dao; 2 | 3 | import com.example.assertion.model.User; 4 | 5 | public class UserRepository { 6 | 7 | public User getUserById(Long userId){ 8 | return null; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/model/AdminUser.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.model; 2 | 3 | import lombok.experimental.SuperBuilder; 4 | 5 | @SuperBuilder 6 | public class AdminUser extends User { 7 | private Boolean isAdmin; 8 | } 9 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/model/Person.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Person { 7 | private String fistName; 8 | private String lastName; 9 | } 10 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/model/Product.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.math.BigDecimal; 8 | import java.util.List; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class Product { 14 | private Long id; 15 | private String name; 16 | private Boolean onSale; 17 | private Integer stockQuantity; 18 | private BigDecimal price; 19 | private List labels; 20 | } 21 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.experimental.SuperBuilder; 6 | 7 | @Data 8 | @SuperBuilder 9 | @AllArgsConstructor 10 | public class User { 11 | private String firstName; 12 | private Integer age; 13 | private Boolean isPremiumUser; 14 | } 15 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/service/FooService.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.service; 2 | 3 | public class FooService { 4 | 5 | public void doStuff(Boolean flag) { 6 | try{ 7 | if(flag){ 8 | // do stuff 9 | } 10 | }catch (Exception e){ 11 | throw new RuntimeException("Unexpected error occurred", e); 12 | } 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/java/com/example/assertion/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion.service; 2 | 3 | import com.example.assertion.dao.UserRepository; 4 | import com.example.assertion.model.User; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Objects; 9 | import java.util.Optional; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | public class UserService { 14 | 15 | private final UserRepository userRepository; 16 | 17 | public Optional getUser(Long userId){ 18 | User user = userRepository.getUserById(userId); 19 | return Objects.nonNull(user) ? Optional.of(user) : Optional.empty(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springboot-assertion/src/main/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/springboot-assertion/src/main/resources/application.yml -------------------------------------------------------------------------------- /springboot-assertion/src/test/java/com/example/assertion/IsPrimeNumber.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion; 2 | 3 | import org.hamcrest.Description; 4 | import org.hamcrest.Matcher; 5 | import org.hamcrest.TypeSafeMatcher; 6 | 7 | import java.util.stream.IntStream; 8 | 9 | public class IsPrimeNumber extends TypeSafeMatcher { 10 | 11 | @Override 12 | protected boolean matchesSafely(Integer number) { 13 | return number > 1 && IntStream.rangeClosed(2, (int) Math.sqrt(number)) 14 | .noneMatch(n -> (number % n == 0)); 15 | } 16 | 17 | @Override 18 | public void describeTo(Description description) { 19 | description.appendText("a prime number"); 20 | } 21 | 22 | public static Matcher isPrimeNumber() { 23 | return new IsPrimeNumber(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /springboot-assertion/src/test/java/com/example/assertion/MyClassTest.java: -------------------------------------------------------------------------------- 1 | package com.example.assertion; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | 6 | public class MyClassTest { 7 | @Test 8 | public void testAddition() { 9 | int result = MyClass.add(2, 3); 10 | assertThat(result).isEqualTo(5); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /springboot-config/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /bin/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | /README.markdown 29 | -------------------------------------------------------------------------------- /springboot-config/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Configuration 2 | This spring boot project is to demonstrate various Configurations available in Spring Boot 3 | 4 | ### Spring Boot @Value Annotation 5 | 1. Inject inline values using `@Value` annotation e.g. [InlineConfig.java](./src/main/java/com/example/demo/config/InlineConfig.java) 6 | 2. Inject values from property file using `@Value` annotation e.g. [CourseConfig.java](./src/main/java/com/example/demo/config/CourseConfig.java) 7 | 3. Inject set of properties using `@ConfigurationProperties` annotation e.g. [PersonConfig.java](./src/main/java/com/example/demo/config/PersonConfig.java). This configuration class file defines different type of properties such as string, int, boolean, float, double, list, object, multi-line string, multi-line-indent string. 8 | 4. Use SpEL (Spring Expression Language) with `@Value` annotation e.g. [ExpressionConfig.java](./src/main/java/com/example/demo/config/ExpressionConfig.java). 9 | 10 | ### Customize Spring Boot Banner 11 | Example usage for custom [banner.txt](./src/main/resources/banner.txt) text to be shown in application startup logs 12 | 13 | ### Custom Actuator Endpoint 14 | Example of custom actuator `release-notes` endpoint implementation [ReleaseNotesEndpoint.java](./src/main/java/com/example/demo/actuator/ReleaseNotesEndpoint.java) 15 | 16 | ### Spring Boot @Conditional Annotation 17 | 1. Create a custom condition implementing `Condition` e.g. [CustomCondition.java](./src/main/java/com/example/demo/condition/CustomCondition.java) 18 | 2. Create combined condition with ANY match by extending `AnyNestedCondition` e.g. [CombinedConditionsWithAnyMatch.java](./src/main/java/com/example/demo/condition/CombinedConditionsWithAnyMatch.java) 19 | 3. Create combined condition with ALL match by extending `AllNestedConditions` e.g. [CombinedConditionsWithAllMatch.java](./src/main/java/com/example/demo/condition/CombinedConditionsWithAllMatch.java) 20 | 4. Create combined condition with NONE match by extending `NoneNestedConditions` e.g. [CombinedConditionsWithNoneMatch.java](./src/main/java/com/example/demo/condition/CombinedConditionsWithNoneMatch.java) 21 | 5. Create custom condition annotation using `@Conditional` e.g. [CustomConditionAnnotation.java](./src/main/java/com/example/demo/condition/CustomConditionAnnotation.java) 22 | 6. Usage of Spring Boot's predefined Conditions `@ConditionalOn...` e.g. [PredefinedConditions.java](./src/main/java/com/example/demo/condition/PredefinedConditions.java) 23 | -------------------------------------------------------------------------------- /springboot-config/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.16' 3 | id 'io.spring.dependency-management' version '1.0.15.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | ext { 16 | set('springCloudVersion', "2021.0.8") 17 | } 18 | 19 | configurations { 20 | all { 21 | exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation('org.springframework.boot:spring-boot-starter-web') { 27 | // exclude group: 'org.springframework.boot', module:'spring-boot-starter-tomcat' 28 | } 29 | //implementation 'org.springframework.boot:spring-boot-starter-jetty' 30 | //implementation 'org.springframework.boot:spring-boot-starter-undertow' 31 | 32 | implementation 'org.springframework.boot:spring-boot-starter-log4j2' 33 | 34 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 35 | implementation 'org.springdoc:springdoc-openapi-ui:latest.release' 36 | 37 | implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' 38 | implementation 'io.github.openfeign:feign-httpclient' 39 | implementation 'io.github.openfeign:feign-okhttp' 40 | 41 | implementation 'commons-io:commons-io:2.6' 42 | 43 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 44 | 45 | compileOnly 'org.projectlombok:lombok:1.18.20' 46 | 47 | annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" 48 | annotationProcessor 'org.projectlombok:lombok:1.18.20' 49 | } 50 | 51 | dependencyManagement { 52 | imports { 53 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 54 | } 55 | } 56 | 57 | test { 58 | useJUnitPlatform() 59 | } -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/SpringBootDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @SpringBootApplication 9 | public class SpringBootDemoApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootDemoApplication.class, args); 13 | } 14 | 15 | @Bean 16 | public RestTemplate getRestTemplate() { 17 | return new RestTemplate(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/actuator/CustomEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.actuator; 2 | 3 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 4 | import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | @Endpoint(id = "custom-endpoint") 9 | public class CustomEndpoint { 10 | 11 | @ReadOperation 12 | public String print() { 13 | return "This is custom management endpoint"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/actuator/ReleaseNotesEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.actuator; 2 | 3 | import lombok.Value; 4 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 5 | import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | 10 | @Component 11 | @Endpoint(id = "release-notes") 12 | public class ReleaseNotesEndpoint { 13 | 14 | @ReadOperation 15 | public ReleaseNotes releaseNotes() { 16 | return new ReleaseNotes(List.of( 17 | new ReleaseNote("1.0", List.of( 18 | "Homepage Added", 19 | "Item creation form added", 20 | "View the watchlist page added")), 21 | new ReleaseNote("2.0", List.of( 22 | "Workspace page Added", 23 | "Add task form created")))); 24 | } 25 | } 26 | 27 | @Value 28 | class ReleaseNotes { 29 | List releaseNotes; 30 | } 31 | 32 | @Value 33 | class ReleaseNote { 34 | String version; 35 | List items; 36 | } 37 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/AnotherCustomCondition.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.context.annotation.Condition; 4 | import org.springframework.context.annotation.ConditionContext; 5 | import org.springframework.core.type.AnnotatedTypeMetadata; 6 | 7 | public class AnotherCustomCondition implements Condition { 8 | 9 | @Override 10 | public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 11 | return context.getEnvironment().getProperty("another-custom.condition.enabled", Boolean.class, false); 12 | } 13 | } -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/CombinedConditionsWithAllMatch.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.boot.autoconfigure.condition.AllNestedConditions; 4 | import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; 5 | import org.springframework.context.annotation.Conditional; 6 | 7 | public class CombinedConditionsWithAllMatch extends AllNestedConditions { 8 | 9 | public CombinedConditionsWithAllMatch() { 10 | super(ConfigurationPhase.REGISTER_BEAN); 11 | } 12 | 13 | @Conditional(CustomCondition.class) 14 | static class OnCustomCondition {} 15 | 16 | @Conditional(AnotherCustomCondition.class) 17 | static class OnAnotherCustomCondition {} 18 | } 19 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/CombinedConditionsWithAnyMatch.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; 4 | import org.springframework.context.annotation.Conditional; 5 | 6 | public class CombinedConditionsWithAnyMatch extends AnyNestedCondition { 7 | 8 | public CombinedConditionsWithAnyMatch() { 9 | super(ConfigurationPhase.PARSE_CONFIGURATION); 10 | } 11 | 12 | @Conditional(CustomCondition.class) 13 | static class OnCustomCondition {} 14 | 15 | @Conditional(AnotherCustomCondition.class) 16 | static class OnAnotherCustomCondition {} 17 | } 18 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/CombinedConditionsWithNoneMatch.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.boot.autoconfigure.condition.AllNestedConditions; 4 | import org.springframework.boot.autoconfigure.condition.NoneNestedConditions; 5 | import org.springframework.context.annotation.Conditional; 6 | 7 | public class CombinedConditionsWithNoneMatch extends NoneNestedConditions { 8 | 9 | public CombinedConditionsWithNoneMatch() { 10 | super(ConfigurationPhase.REGISTER_BEAN); 11 | } 12 | 13 | @Conditional(CustomCondition.class) 14 | static class OnCustomCondition {} 15 | 16 | @Conditional(AnotherCustomCondition.class) 17 | static class OnAnotherCustomCondition {} 18 | } 19 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/CustomCondition.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.context.annotation.Condition; 4 | import org.springframework.context.annotation.ConditionContext; 5 | import org.springframework.core.type.AnnotatedTypeMetadata; 6 | 7 | public class CustomCondition implements Condition { 8 | 9 | @Override 10 | public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 11 | return context.getEnvironment().getProperty("custom.condition.enabled", Boolean.class, false); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/CustomConditionAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.context.annotation.Conditional; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Target({ElementType.TYPE, ElementType.METHOD}) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Conditional(CustomCondition.class) 13 | public @interface CustomConditionAnnotation { 14 | } 15 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/condition/PredefinedConditions.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.condition; 2 | 3 | import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; 4 | import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; 5 | import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort; 6 | import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; 7 | import org.springframework.boot.actuate.info.InfoEndpoint; 8 | import org.springframework.boot.autoconfigure.condition.*; 9 | import org.springframework.boot.cloud.CloudPlatform; 10 | import org.springframework.boot.system.JavaVersion; 11 | 12 | @ConditionalOnClass(name = "com.example.demo.DoesNotExist") 13 | @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) 14 | @ConditionalOnJava(JavaVersion.EIGHT) 15 | @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) 16 | @ConditionalOnWarDeployment 17 | @ConditionalOnJndi("java:comp/env/ejb/myEJB") 18 | @ConditionalOnSingleCandidate 19 | @ConditionalOnManagementPort(ManagementPortType.DIFFERENT) 20 | @ConditionalOnAvailableEndpoint(endpoint = InfoEndpoint.class) 21 | @ConditionalOnEnabledHealthIndicator(value = "heartbeat") 22 | public class PredefinedConditions { 23 | } 24 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/config/CustomServletConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 4 | import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | public class CustomServletConfig { 8 | 9 | @Bean 10 | public WebServerFactoryCustomizer webServerFactoryCustomizer() { 11 | return factory -> factory.setContextPath("/myapp"); 12 | } 13 | 14 | /* For Spring Boot 1.x 15 | @Bean 16 | public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() { 17 | return container -> container.setContextPath("/myapp"); 18 | } 19 | */ 20 | } 21 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/config/ExpressionConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Data; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | import java.util.List; 10 | 11 | @Configuration 12 | @Data 13 | @JsonIgnoreProperties({"$$beanFactory"}) 14 | public class ExpressionConfig { 15 | 16 | @Value("#{new java.net.URI('${bootstrap.url}').getScheme()}") 17 | private String scheme; 18 | 19 | @Value("#{new java.net.URI('${bootstrap.url}').getHost()}") 20 | private String host; 21 | 22 | @Value("#{new java.net.URI('${bootstrap.url}').getPort()}") 23 | private Integer port; 24 | 25 | @Value("#{'Java, JavaScript, Python, Ruby'.split(',')}") 26 | private List languages; 27 | 28 | @Value("#{'Java, JavaScript, Python, Ruby'.split(',')[0]}") 29 | private String firstLanguage; 30 | 31 | @Value("#{((1 + 2^3 - 4) * (5 % 6)) / 7 }") // 3.0 32 | private Double arithmeticOperation; 33 | 34 | @Value("#{((1 + 2^3 - 4) * (5 mod 6)) div 7 }") // 3.0 35 | private Double anotherArithmeticOperation; 36 | 37 | @Value("#{'Hello ' + 'World'}") // "Hello World" 38 | private String concatString; 39 | 40 | // @Value("#{1 == 1}") true 41 | @Value("#{1 eq 1}") // true 42 | private boolean equal; 43 | 44 | //@Value("#{1 != 1}") // false 45 | @Value("#{1 ne 1}") // false 46 | private boolean notEqual; 47 | 48 | // @Value("#{1 < 1}") // false 49 | @Value("#{1 lt 1}") // false 50 | private boolean lessThan; 51 | 52 | //@Value("#{1 <= 1}") // true 53 | @Value("#{1 le 1}") // true 54 | private boolean lessThanOrEqual; 55 | 56 | //@Value("#{1 > 1}") // false 57 | @Value("#{1 gt 1}") // false 58 | private boolean greaterThan; 59 | 60 | //@Value("#{1 >= 1}") // true 61 | @Value("#{1 ge 1}") // true 62 | private boolean greaterThanOrEqual; 63 | 64 | //@Value("#{250 > 200 && 200 < 4000}") // true 65 | @Value("#{250 > 200 and 200 < 4000}") // true 66 | private boolean andOperation; 67 | 68 | //@Value("#{400 > 300 || 150 < 100}") // true 69 | @Value("#{400 > 300 or 150 < 100}") // true 70 | private boolean orOperation; 71 | 72 | //@Value("#{!true}") // false 73 | @Value("#{not true}") // false 74 | private boolean notOperation; 75 | 76 | @Value("#{2 > 1 ? 'a' : 'b'}") // "a" 77 | private String ternaryOperator; 78 | 79 | @Autowired 80 | private InlineConfig inlineConfig; 81 | 82 | @Value("#{inlineConfig.title != null ? inlineConfig.title : 'default'}") 83 | private String nullCheckUsingTernaryOperator; 84 | 85 | @Value("#{inlineConfig.title ?: 'default'}") // Will inject provided string if someProperty is null 86 | private String nullCheckUsingElvisOperator; 87 | 88 | } 89 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/config/FeignClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import okhttp3.OkHttpClient; 4 | import org.apache.http.impl.client.CloseableHttpClient; 5 | import org.apache.http.impl.client.HttpClients; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class FeignClientConfig { 11 | 12 | 13 | @Bean 14 | public CloseableHttpClient feignClient() { 15 | return HttpClients.createDefault(); 16 | } 17 | 18 | /* 19 | @Bean 20 | public OkHttpClient feignClient() { 21 | return new OkHttpClient(); 22 | } 23 | */ 24 | 25 | } 26 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/config/InlineConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Data; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import java.time.LocalDate; 9 | import java.time.LocalDateTime; 10 | import java.util.Date; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @Configuration 15 | @Data 16 | @JsonIgnoreProperties({"$$beanFactory"}) 17 | public class InlineConfig { 18 | 19 | @Value("How to use @Value Annotation with inline values") 20 | private String title; 21 | 22 | @Value("30") 23 | private Integer duration; 24 | 25 | @Value("4.5") 26 | private Float rating; 27 | 28 | @Value("1e+10") 29 | private Double pageViews; 30 | 31 | @Value("true") 32 | private Boolean isTrending; 33 | 34 | @Value("Spring, Spring Boot, Annotation") 35 | private List tags; 36 | 37 | // SpEL expression used to initialize a Map 38 | @Value("#{{'keyword1': '12', 'keyword2': '44', 'keyword3': '85', 'keyword4': '100'}}") 39 | private Map keywordCountMap; 40 | 41 | // Inject Date with given format using SpEL expression 42 | @Value("#{new java.text.SimpleDateFormat('yyyyMMdd').parse('20210530')}") 43 | private Date createdDate; 44 | 45 | // Inject LocalDate with ISO_DATE format using SpEL expression 46 | @Value("#{T(java.time.LocalDate).parse('2021-05-31')}") 47 | private LocalDate updatedDate; 48 | 49 | // Inject LocalDateTime with ISO_LOCAL_DATE_TIME format using SpEL expression 50 | @Value("#{T(java.time.LocalDateTime).parse('2015-08-04T10:11:30')}") 51 | private LocalDateTime lastAccess; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Contact; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 11 | import org.springframework.boot.context.properties.ConfigurationProperties; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | 15 | @Configuration 16 | @ConfigurationProperties("info.app") 17 | @Getter 18 | @Setter 19 | @ConditionalOnProperty(name = "springdoc.swagger-ui.enabled", havingValue = "true", matchIfMissing = true) 20 | //@ConditionalOnExpression("${springdoc.swagger-ui.enabled:true} and '${spring.profile.active}'.equalsIgnoreCase('DEV')") 21 | public class OpenApiConfig { 22 | 23 | private String title; 24 | private String description; 25 | private String version; 26 | private String termOfService; 27 | private String contactName; 28 | private String contactEmail; 29 | private String contactUrl; 30 | private String licenseName; 31 | private String licenseUrl; 32 | 33 | @Bean 34 | public OpenAPI api() { 35 | return new OpenAPI() 36 | .info(info()); 37 | } 38 | 39 | private Info info() { 40 | return new Info() 41 | .title(title) 42 | .description(description) 43 | .version(version) 44 | .contact(new Contact().name(contactName).email(contactEmail).url(contactUrl)) 45 | .license(new License().name(licenseName).url(licenseUrl)); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/config/PersonConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import lombok.Data; 11 | 12 | @Configuration 13 | @ConfigurationProperties("person") 14 | @Data 15 | @JsonIgnoreProperties({"$$beanFactory"}) 16 | public class PersonConfig { 17 | 18 | private String name; 19 | private String occupation; 20 | private int age; 21 | private float gpa; 22 | private double favNum; 23 | private boolean male; 24 | private String birthday; 25 | private String flaws; 26 | private String[] hobbies; 27 | private List movies; 28 | private Map assets; 29 | private Map> size; 30 | private List friends; 31 | private String description; 32 | private String signature; 33 | } 34 | 35 | @Data 36 | class Friend { 37 | private String name; 38 | private int age; 39 | } 40 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/controller/ConditionalController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import com.example.demo.condition.CombinedConditionsWithAnyMatch; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 5 | import org.springframework.context.annotation.Conditional; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | @RequestMapping("/condition") 12 | @Conditional(CombinedConditionsWithAnyMatch.class) 13 | public class ConditionalController { 14 | 15 | @GetMapping 16 | public String print() { 17 | return "Custom condition is matched"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/controller/ConfigController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import com.example.demo.config.*; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | @RequestMapping("/config") 11 | public class ConfigController { 12 | 13 | @Autowired 14 | private InlineConfig inlineConfig; 15 | 16 | @Autowired 17 | private CourseConfig courseConfig; 18 | 19 | @Autowired 20 | private PersonConfig personConfig; 21 | 22 | @Autowired 23 | private ExpressionConfig expressionConfig; 24 | 25 | @GetMapping("/inline") 26 | private InlineConfig getInlineConfig() { 27 | System.out.println(inlineConfig); 28 | return inlineConfig; 29 | } 30 | 31 | @GetMapping("/course") 32 | private CourseConfig getCourseConfig() { 33 | System.out.println(courseConfig); 34 | return courseConfig; 35 | } 36 | 37 | @GetMapping("/person") 38 | private PersonConfig getPersonConfig() { 39 | System.out.println(personConfig); 40 | return personConfig; 41 | } 42 | 43 | @GetMapping("/expression") 44 | private ExpressionConfig getListConfig() { 45 | System.out.println(expressionConfig); 46 | return expressionConfig; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/controller/PostController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import com.example.demo.model.Post; 4 | import com.example.demo.service.PostService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.List; 12 | 13 | @RestController 14 | @RequiredArgsConstructor 15 | public class PostController { 16 | 17 | private final PostService postService; 18 | 19 | @GetMapping("/posts") 20 | public List getAllPosts() { 21 | return postService.getAllPosts(); 22 | } 23 | 24 | @GetMapping("/posts/user/{userId}") 25 | public List getAllPostsByUserId(@PathVariable Long userId) { 26 | return postService.getAllPostsByUserId(userId); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/model/Post.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | public class Post { 4 | 5 | public Post(int id, String title, String body){ 6 | this.id = id; 7 | this.title = title; 8 | this.body = body; 9 | } 10 | 11 | public int id; 12 | public String title; 13 | public String body; 14 | 15 | public int getId() { 16 | return id; 17 | } 18 | public void setId(int id) { 19 | this.id = id; 20 | } 21 | public String getTitle() { 22 | return title; 23 | } 24 | public void setTitle(String title) { 25 | this.title = title; 26 | } 27 | public String getBody() { 28 | return body; 29 | } 30 | public void setBody(String body) { 31 | this.body = body; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/service/PostService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.Post; 4 | 5 | import java.util.List; 6 | 7 | public interface PostService { 8 | 9 | List getAllPosts(); 10 | 11 | List getAllPostsByUserId(Long userId); 12 | } 13 | -------------------------------------------------------------------------------- /springboot-config/src/main/java/com/example/demo/service/PostServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.Post; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Objects; 11 | 12 | @Service 13 | public class PostServiceImpl implements PostService { 14 | 15 | @Autowired 16 | private RestTemplate restTemplate; 17 | 18 | public List getAllPosts() { 19 | return Arrays.asList( 20 | new Post(1, "Spring Boot", "All about Spring boot microservice"), 21 | new Post(2, "Java", "Learn Streams in Java"), 22 | new Post(3, "JavaScript", "Whats new in ES6") 23 | ); 24 | } 25 | 26 | public List getAllPostsByUserId(Long userId) { 27 | Post[] posts = restTemplate.getForObject("https://jsonplaceholder.typicode.com/posts/" + userId, Post[].class); 28 | return Arrays.asList(Objects.requireNonNull(posts)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /springboot-config/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | course.title = How to use Spring @Value annotation 2 | course.duration = 30 3 | course.rating = 4.5 4 | course.page_views = 1e+10 5 | course.trending = true 6 | course.tags = Spring,Spring Boot,Annotation 7 | course.keyword_count = { 'keyword1': '12', 'keyword2': '44', 'keyword3': '85', 'keyword4': '100'} 8 | course.created_date = 20210530 9 | course.updated_date = 2021-05-31 10 | course.last_access = 2015-08-04T10:11:30 11 | course.label.NAME = Course Title 12 | course.label.DESCRIPTION = Course Description 13 | bootstrap.url = http://localhost:9091 -------------------------------------------------------------------------------- /springboot-config/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ____ _ _ _ ____ _ 2 | / ___|___ __| (_)_ __ __ _ | \ | | / ___|___ _ __ ___ ___ _ __ | |_ ___ 3 | | | / _ \ / _` | | '_ \ / _` | | \| | | | / _ \| '_ \ / __/ _ \ '_ \| __/ __| 4 | | |__| (_) | (_| | | | | | (_| | | |\ | | |__| (_) | | | | (_| __/ |_) | |_\__ \ 5 | \____\___/ \__,_|_|_| |_|\__, | |_| \_| \____\___/|_| |_|\___\___| .__/ \__|___/ 6 | |___/ |_| -------------------------------------------------------------------------------- /springboot-config/src/test/java/com/example/demo/config/CourseConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import static org.assertj.core.api.Assertions.atIndex; 8 | 9 | @SpringBootTest 10 | public class CourseConfigTest { 11 | 12 | @Autowired 13 | CourseConfig courseConfig; 14 | 15 | @Test 16 | public void testCourseConfigProperties(){ 17 | assertThat(courseConfig.getTitle()).isEqualTo("How to use Spring @Value annotation"); 18 | assertThat(courseConfig.getDuration()).isEqualTo(30); 19 | assertThat(courseConfig.getRating()).isEqualTo(4.5f); 20 | assertThat(courseConfig.isTrending()).isTrue(); 21 | assertThat(courseConfig.getTags()).contains("Spring", atIndex(0)); 22 | assertThat(courseConfig.getTags()).contains("Spring Boot", atIndex(1)); 23 | assertThat(courseConfig.getTags()).contains("Annotation", atIndex(2)); 24 | assertThat(courseConfig.getKeywordCountMap()).containsEntry("keyword1", 12); 25 | assertThat(courseConfig.getKeywordCountMap()).containsEntry("keyword2", 44); 26 | assertThat(courseConfig.getLabels()).containsEntry("NAME", "Course Title"); 27 | assertThat(courseConfig.getLabels()).containsEntry("DESCRIPTION", "Course Description"); 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /springboot-email/.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle ### 2 | .gradle 3 | /build/ 4 | 5 | ### Maven ### 6 | /target/ 7 | /bin/ 8 | !.mvn/wrapper/maven-wrapper.jar 9 | 10 | ### Spring Tool Suite ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBean 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | /out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbnuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | 36 | ### Logs ### 37 | *.logs 38 | 39 | ### Mac ### 40 | *.DS_Store -------------------------------------------------------------------------------- /springboot-email/README.md: -------------------------------------------------------------------------------- 1 | # springboot-email 2 | 3 | ## Overview 4 | Email Service to compose emails using Thymeleaf HTML template and send them using spring framework's JavaMailSender 5 | -------------------------------------------------------------------------------- /springboot-email/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | ext { 16 | set('springCloudVersion', "2020.0.3") 17 | } 18 | 19 | dependencies { 20 | implementation 'org.springframework.boot:spring-boot-starter-web' 21 | implementation 'org.springdoc:springdoc-openapi-ui:latest.release' 22 | implementation 'org.springframework.boot:spring-boot-starter-mail' 23 | implementation 'org.thymeleaf:thymeleaf' 24 | implementation 'org.thymeleaf:thymeleaf-spring5' 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | 27 | compileOnly 'org.projectlombok:lombok:1.18.20' 28 | annotationProcessor 'org.projectlombok:lombok:1.18.20' 29 | } 30 | 31 | dependencyManagement { 32 | imports { 33 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 34 | } 35 | } 36 | 37 | test { 38 | useJUnitPlatform() 39 | } -------------------------------------------------------------------------------- /springboot-email/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.5.0 9 | 10 | 11 | com.example 12 | api 13 | 0.0.1-SNAPSHOT 14 | springboot-email 15 | Email Service with Spring Boot 16 | 17 | 11 18 | 2020.0.3 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-mail 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-thymeleaf 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | 41 | org.projectlombok 42 | lombok 43 | provided 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 57 | ${java.version} 58 | ${java.version} 59 | 60 | 61 | org.projectlombok 62 | lombok 63 | 64 | 65 | org.mapstruct 66 | mapstruct-processor 67 | 68 | 69 | 70 | -Amapstruct.suppressGeneratorTimestamp=true 71 | -Amapstruct.defaultComponentModel=spring 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /springboot-email/src/main/java/com/example/email/EmailApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.email; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class EmailApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(EmailApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springboot-email/src/main/java/com/example/email/config/ThymeleafTemplateConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.email.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.thymeleaf.spring5.SpringTemplateEngine; 6 | import org.thymeleaf.templatemode.TemplateMode; 7 | import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; 8 | 9 | import java.nio.charset.StandardCharsets; 10 | 11 | @Configuration 12 | public class ThymeleafTemplateConfig { 13 | 14 | @Bean 15 | public SpringTemplateEngine springTemplateEngine() { 16 | SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine(); 17 | springTemplateEngine.addTemplateResolver(emailTemplateResolver()); 18 | return springTemplateEngine; 19 | } 20 | 21 | public ClassLoaderTemplateResolver emailTemplateResolver() { 22 | ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver(); 23 | emailTemplateResolver.setPrefix("/templates/"); 24 | emailTemplateResolver.setSuffix(".html"); 25 | emailTemplateResolver.setTemplateMode(TemplateMode.HTML); 26 | emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name()); 27 | emailTemplateResolver.setCacheable(false); 28 | return emailTemplateResolver; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /springboot-email/src/main/java/com/example/email/model/Email.java: -------------------------------------------------------------------------------- 1 | package com.example.email.model; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.util.Map; 7 | 8 | @Data 9 | public class Email { 10 | @Schema(example = "lahoti.ashish20@gmail.com") 11 | String to; 12 | @Schema(example = "lahoti.ashish20@gmail.com") 13 | String from; 14 | @Schema(example = "Welcome Email from CodingNConcepts") 15 | String subject; 16 | @Schema(example = "Thank you for subscribing to our channel.") 17 | String text; 18 | @Schema(example = "welcome-email.html") 19 | String template; 20 | @Schema(example = "{\n" + 21 | "\"name\": \"Ashish\",\n" + 22 | "\"subscriptionDate\": \"28-12-2012\",\n" + 23 | " \"technologies\": [\"java\", \"javascript\"]\n" + 24 | "}") 25 | Map properties; 26 | } 27 | -------------------------------------------------------------------------------- /springboot-email/src/main/java/com/example/email/service/EmailSenderController.java: -------------------------------------------------------------------------------- 1 | package com.example.email.service; 2 | 3 | import com.example.email.model.Email; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestBody; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import javax.mail.MessagingException; 10 | 11 | @RestController 12 | @RequiredArgsConstructor 13 | public class EmailSenderController { 14 | 15 | private final EmailSenderService emailSenderService; 16 | 17 | @PostMapping("/email/send/html") 18 | public void sendHtmlMessage(@RequestBody Email email) throws MessagingException { 19 | emailSenderService.sendHtmlMessage(email); 20 | } 21 | 22 | @PostMapping("email/send") 23 | public void sendSimpleMessage(@RequestBody Email email) { 24 | emailSenderService.sendSimpleMessage(email); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /springboot-email/src/main/java/com/example/email/service/EmailSenderService.java: -------------------------------------------------------------------------------- 1 | package com.example.email.service; 2 | 3 | import com.example.email.model.Email; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.mail.SimpleMailMessage; 7 | import org.springframework.mail.javamail.JavaMailSender; 8 | import org.springframework.mail.javamail.MimeMessageHelper; 9 | import org.springframework.stereotype.Service; 10 | import org.thymeleaf.context.Context; 11 | import org.thymeleaf.spring5.SpringTemplateEngine; 12 | 13 | import javax.mail.MessagingException; 14 | import javax.mail.internet.MimeMessage; 15 | import java.nio.charset.StandardCharsets; 16 | 17 | @Service 18 | @RequiredArgsConstructor 19 | @Slf4j 20 | public class EmailSenderService { 21 | 22 | private final JavaMailSender emailSender; 23 | private final SpringTemplateEngine templateEngine; 24 | 25 | public void sendHtmlMessage(Email email) throws MessagingException { 26 | MimeMessage message = emailSender.createMimeMessage(); 27 | MimeMessageHelper helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, StandardCharsets.UTF_8.name()); 28 | Context context = new Context(); 29 | context.setVariables(email.getProperties()); 30 | helper.setFrom(email.getFrom()); 31 | helper.setTo(email.getTo()); 32 | helper.setSubject(email.getSubject()); 33 | String html = templateEngine.process(email.getTemplate(), context); 34 | helper.setText(html, true); 35 | 36 | log.info("Sending email: {} with html body: {}", email, html); 37 | emailSender.send(message); 38 | } 39 | 40 | public void sendSimpleMessage(Email email) { 41 | SimpleMailMessage message = new SimpleMailMessage(); 42 | message.setFrom(email.getFrom()); 43 | message.setTo(email.getTo()); 44 | message.setSubject(email.getSubject()); 45 | message.setText(email.getText()); 46 | 47 | log.info("Sending email: {} with text body: {}", email, email.getText()); 48 | emailSender.send(message); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /springboot-email/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | springdoc: 2 | swagger-ui: 3 | enabled: true 4 | path: / # Redirect localhost:8080 to swagger-ui.html 5 | 6 | server: 7 | port: 8080 8 | 9 | spring: 10 | mail: 11 | default-encoding: UTF-8 12 | host: smtp.gmail.com 13 | username: gmail_account 14 | password: gmail_app_password 15 | port: 587 16 | properties: 17 | mail: 18 | smtp: 19 | auth: true 20 | starttls: 21 | enable: true 22 | debug: true 23 | protocol: smtp 24 | test-connection: false 25 | -------------------------------------------------------------------------------- /springboot-email/src/main/resources/templates/welcome-email.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Template for HTML email with inline image 5 | 6 | 7 | 8 |

9 | Hello, Peter Static! 10 |

11 |

12 | Wow! You've got a long name (more than 10 chars)! 13 |

14 |

15 | You have been successfully subscribed to the CodingNConcepts on 16 | 28-12-2012 17 |

18 |

We write on following technologies:-

19 |
    20 |
  • Java
  • 21 |
  • JavaScript
  • 22 |
  • CSS
  • 23 |
24 |

25 | Regards,
26 | The CodingNConcepts Team 27 |

28 | 29 | -------------------------------------------------------------------------------- /springboot-email/src/test/java/com/example/email/EmailApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.email; 2 | 3 | import com.example.email.service.EmailSenderService; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @SpringBootTest 11 | public class EmailApplicationTests { 12 | 13 | @Autowired 14 | private EmailSenderService emailSenderService; 15 | 16 | @Test 17 | void contextLoads() { 18 | assertThat(emailSenderService).isNotNull(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /springboot-email/src/test/java/com/example/email/service/EmailSenderServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.email.service; 2 | 3 | import com.example.email.model.Email; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | import javax.mail.MessagingException; 10 | import java.time.LocalDate; 11 | import java.util.Arrays; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | @SpringBootTest 16 | public class EmailSenderServiceTest { 17 | 18 | @Autowired 19 | private EmailSenderService emailSenderService; 20 | 21 | @Test 22 | public void sendHtmlMessageTest() { 23 | Email email = new Email(); 24 | email.setTo("lahoti.ashish20@gmail.com"); 25 | email.setFrom("lahoti.ashish20@gmail.com"); 26 | email.setSubject("Welcome Email from CodingNConcepts"); 27 | email.setTemplate("welcome-email.html"); 28 | Map properties = new HashMap<>(); 29 | properties.put("name", "Ashish"); 30 | properties.put("subscriptionDate", LocalDate.now().toString()); 31 | properties.put("technologies", Arrays.asList("Python", "Go", "C#")); 32 | email.setProperties(properties); 33 | 34 | Assertions.assertDoesNotThrow(() -> emailSenderService.sendHtmlMessage(email)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /springboot-jpa/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /bin/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | /README.markdown 29 | -------------------------------------------------------------------------------- /springboot-jpa/README.md: -------------------------------------------------------------------------------- 1 | ![GitHub lastest release version](https://img.shields.io/github/v/release/ashishlahoti/springboot-demo?label=AppVersion) 2 | ![GitHub contributors](https://img.shields.io/github/contributors/ashishlahoti/springboot-demo) 3 | ![GitHub last commit](https://img.shields.io/github/last-commit/ashishlahoti/springboot-demo) 4 | ![GitHub Release Date](https://img.shields.io/github/release-date/ashishlahoti/springboot-demo) 5 | ![GitHub All Releases](https://img.shields.io/github/downloads/ashishlahoti/springboot-demo/total) 6 | 7 | # Spring Boot Starter 8 | This springboot starter project is using jpa and web modules of spring boot to interact with database and exposing REST endpoints for distribution. It is using some userful libraries which works very well with springboot and makes your development easy. 9 | 10 | ### Database Encyption 11 | Database password encryption in application.properties / application.yml 12 | If you want to configure encrypted password in the application.properties or application.yml file then you need to configure DataSource bean by yourself. See **[PersistenceConfig.java](src/main/java/com/example.jpa/demo/config/PersistenceConfig.java)**. where you can decrypt the password using your own implemention and configure custom DataSource bean. 13 | 14 | ### Lombok 15 | Using Lombok library is very userful which auto generates getter/setter and implements toString() and hashCode() methods of your model libraries at compile time. It keeps the model classes neat and clean. See the usage of @Data annotation in entity **[User.java](src/main/java/com/example.jpa/demo/dao/entity/User.java)** and model classes. 16 | >You need to install lombok plugin in IDE tool. For eclipse download the [lombok.jar](https://projectlombok.org/download) 17 | From terminal execute the jar 18 | ``` 19 | $ java -jar lombok.jar 20 | ``` 21 | 22 | ### Swagger 23 | Using Swagger library is very useful if you are creating REST endpoints. It generates a GUI for you to execute GET, POST, PUT, DELETE HTTP endpoints. Isn't that cool. GUI is accessible using following URL:- http://localhost:8080/swagger-ui.html 24 | 25 | ### Mapstruct 26 | Mapstruct library is one of the best model-mapping library available in the market based on performance. It is useful to auto generate model mapping (e.g. DTO to entity, entity to DTO) classes at compile time and keeps your source code neat and clean. See how @Mapper(componentModel="spring") annotation is being usage in **[UserModelMapper.java](src/main/java/com/example.jpa/demo/model/mapper/UserModelMapper.java)** which generates UserModelMapperImpl.class file at compile time and also create a bean with name userModelMapper. Later this model mapper dependency is injected in **[UserServiceImpl.java](src/main/java/com/example.jpa/demo/service/UserServiceImpl.java)** using @Autowired annotation for model-mapping. 27 | 28 | ### Logging 29 | 1. Use @Slf4J annotation for logging 30 | 2. Use **[ApiLoggingFilterConfig.java](src/main/java/com/example.jpa/demo/config/ApiLoggingFilterConfig.java)** to log HTTP Rest request and response for configurable url-patterns 31 | 32 | ### Config 33 | 1. Use @ConfigurationProperties to define a set of properties for e.g. **[PersonConfig.java](src/main/java/com/example.jpa/demo/config/PersonConfig.java)**. This configuration class file defines different type of properties such as string, int, boolean, float, double, list, object, multi-line string, multi-line-indent string. 34 | 2. Use @Value properties to define single property for e.g. **[ConfigController.java](src/main/java/com/example.jpa/demo/controller/ConfigController.java)** 35 | -------------------------------------------------------------------------------- /springboot-jpa/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | ext { 16 | set('springCloudVersion', "2020.0.3") 17 | } 18 | 19 | dependencies { 20 | implementation 'org.springframework.boot:spring-boot-starter-web' 21 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 22 | implementation 'commons-io:commons-io:2.11.0' 23 | implementation 'org.mapstruct:mapstruct-jdk8:1.3.0.Beta1' 24 | 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | 27 | compileOnly 'org.projectlombok:lombok:1.18.24' 28 | annotationProcessor 'org.projectlombok:lombok:1.18.24' 29 | 30 | runtimeOnly 'com.h2database:h2' 31 | } 32 | 33 | dependencyManagement { 34 | imports { 35 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 36 | } 37 | } 38 | 39 | test { 40 | useJUnitPlatform() 41 | } -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/SpringBootJpaApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @SpringBootApplication 9 | public class SpringBootJpaApplication { 10 | 11 | @Bean 12 | public RestTemplate getRestTemplate() { 13 | return new RestTemplate(); 14 | } 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(com.example.jpa.SpringBootJpaApplication.class, args); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/config/PersistenceConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.config; 2 | 3 | import com.example.jpa.service.CryptoService; 4 | import com.zaxxer.hikari.HikariDataSource; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Primary; 11 | 12 | @Configuration 13 | public class PersistenceConfig { 14 | 15 | @Autowired 16 | private CryptoService cryptoService; 17 | 18 | @Bean 19 | @Primary 20 | @ConfigurationProperties("spring.datasource") 21 | public DataSourceProperties dataSourceProperties() { 22 | return new DataSourceProperties(); 23 | } 24 | 25 | @Bean 26 | @ConfigurationProperties("spring.datasource.hikari") 27 | public HikariDataSource dataSource(DataSourceProperties properties) { 28 | properties.setPassword(cryptoService.decrypt(properties.getPassword())); 29 | return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); 30 | } 31 | } -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/controller/PostController.java: -------------------------------------------------------------------------------- 1 | package com.abc.controller; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | 12 | @RequestMapping("/posts") 13 | public class PostController { 14 | 15 | @GetMapping 16 | public List getAllPosts() { 17 | 18 | return Arrays.asList(new Post[] { 19 | new Post(1, "Spring Boot", "All about Spring boot microservice"), 20 | new Post(2, "Java", "Learn Streams in Java"), 21 | new Post(3, "JavaScript", "Whats new in ES6") 22 | }); 23 | } 24 | } 25 | 26 | class Post { 27 | 28 | Post(int id, String title, String body){ 29 | this.id = id; 30 | this.title = title; 31 | this.body = body; 32 | } 33 | 34 | public int id; 35 | public String title; 36 | public String body; 37 | 38 | public int getId() { 39 | return id; 40 | } 41 | public void setId(int id) { 42 | this.id = id; 43 | } 44 | public String getTitle() { 45 | return title; 46 | } 47 | public void setTitle(String title) { 48 | this.title = title; 49 | } 50 | public String getBody() { 51 | return body; 52 | } 53 | public void setBody(String body) { 54 | this.body = body; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.controller; 2 | 3 | import com.example.jpa.dao.entity.User; 4 | import com.example.jpa.model.query.UserQueryModel; 5 | import com.example.jpa.model.request.UserRequestModel; 6 | import com.example.jpa.service.UserService; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.domain.Page; 10 | import org.springframework.data.domain.Pageable; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.*; 13 | import org.springframework.web.client.ResourceAccessException; 14 | 15 | @RestController 16 | @Slf4j 17 | @RequestMapping("/users") 18 | public class UserController { 19 | 20 | @Autowired 21 | private UserService userService; 22 | 23 | @GetMapping 24 | public Page getAllUsers(UserQueryModel userQueryModel, Pageable pageable) { 25 | log.debug("getAllUsers: {}, page: {}", userQueryModel, pageable); 26 | return userService.getAllUsers(userQueryModel, pageable); 27 | } 28 | 29 | @GetMapping("/{id}") 30 | public User getUserById(@PathVariable Long id) { 31 | return userService.getUserById(id).orElseThrow(() -> new ResourceAccessException("User id not found")); 32 | } 33 | 34 | @PostMapping 35 | public User createUser(@RequestBody UserRequestModel userRequestModel) { 36 | return userService.createUser(userRequestModel); 37 | } 38 | 39 | @PutMapping("/{id}") 40 | public User updateUser(@PathVariable Long id, UserRequestModel userRequestModel) { 41 | return userService.updateUser(id, userRequestModel); 42 | } 43 | 44 | @DeleteMapping("/{id}") 45 | public ResponseEntity deleteUserById(@PathVariable Long id) { 46 | userService.getUserById(id).orElseThrow(() -> new ResourceAccessException("User id not found")); 47 | userService.deleteUserById(id); 48 | return ResponseEntity.ok().build(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/controller/UserPostController.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.controller; 2 | 3 | import com.example.jpa.dao.entity.Post; 4 | import com.example.jpa.service.UserPostService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | @RestController 15 | @Slf4j 16 | @RequestMapping("/userpost") 17 | public class UserPostController { 18 | 19 | @Autowired 20 | private UserPostService userPostService; 21 | 22 | @GetMapping("/{userId}") 23 | public List getAllPostsByUserId(@PathVariable Long userId) { 24 | log.debug("getAllPostsByUserId: {}", userId); 25 | return userPostService.getAllPostsByUserId(userId); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/entity/Post.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.entity; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Post { 7 | 8 | private Long userId; 9 | 10 | private Long id; 11 | 12 | private String title; 13 | 14 | private String body; 15 | } 16 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.entity; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import javax.persistence.CascadeType; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.GenerationType; 10 | import javax.persistence.Id; 11 | import javax.persistence.ManyToMany; 12 | 13 | import lombok.Data; 14 | import lombok.EqualsAndHashCode; 15 | 16 | @Entity 17 | @Data 18 | @EqualsAndHashCode(exclude = "userGroups") 19 | public class User { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.AUTO) 23 | private Long id; 24 | 25 | private String name; 26 | 27 | private Integer age; 28 | 29 | @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}) 30 | private Set userGroups = new HashSet<>(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/entity/UserGroup.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.entity; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.ManyToMany; 11 | 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | 15 | @Entity 16 | @Data 17 | @EqualsAndHashCode(exclude = "users") 18 | public class UserGroup { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.AUTO) 22 | private Long id; 23 | 24 | private String name; 25 | 26 | @ManyToMany(mappedBy="userGroups") 27 | private Set users = new HashSet<>(); 28 | } 29 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/repository/UserGroupRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.repository; 2 | 3 | import com.example.jpa.dao.entity.UserGroup; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 | 7 | public interface UserGroupRepository extends JpaRepository, JpaSpecificationExecutor { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.repository; 2 | 3 | import com.example.jpa.dao.entity.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 | import org.springframework.stereotype.Repository; 7 | 8 | @Repository 9 | public interface UserRepository extends JpaRepository, JpaSpecificationExecutor { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/spec/BaseSpecification.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.spec; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.data.jpa.domain.Specification; 6 | 7 | public abstract class BaseSpecification { 8 | 9 | public Specification findByField(String fieldName, String fieldValue) { 10 | return (root, query, cb) -> (fieldValue == null || fieldValue.isEmpty()) 11 | ? null 12 | : cb.equal(root.get(fieldName), fieldValue); 13 | } 14 | 15 | public Specification findBetweenDates(String fieldName, Date fromDate, Date toDate) { 16 | return (root, query, cb) -> (fromDate == null || toDate==null) 17 | ? null 18 | : cb.between(root.get(fieldName), toDate, fromDate); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/dao/spec/UserSpecification.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.dao.spec; 2 | 3 | import com.example.jpa.dao.entity.User; 4 | import com.example.jpa.model.query.UserQueryModel; 5 | import org.springframework.data.jpa.domain.Specification; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class UserSpecification extends BaseSpecification { 10 | 11 | public Specification findByUserQueryModel(UserQueryModel userQueryModel) { 12 | return findByField("name", userQueryModel.getName()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/model/mapper/UserModelMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.model.mapper; 2 | 3 | import com.example.jpa.dao.entity.User; 4 | import com.example.jpa.model.request.UserRequestModel; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class UserModelMapper { 9 | 10 | public User toUserEntity(UserRequestModel userRequestModel){ 11 | User user = new User(); 12 | user.setName(userRequestModel.getName()); 13 | user.setAge(userRequestModel.getAge()); 14 | return user; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/model/query/UserQueryModel.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.model.query; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class UserQueryModel { 7 | 8 | private String name; 9 | } 10 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/model/request/UserRequestModel.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.model.request; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class UserRequestModel { 7 | 8 | private String name; 9 | private Integer age; 10 | } 11 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/service/CryptoService.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.service; 2 | 3 | public interface CryptoService { 4 | 5 | String encrypt(String password); 6 | 7 | String decrypt(String password); 8 | } 9 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/service/CryptoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class CryptoServiceImpl implements CryptoService { 7 | 8 | @Override 9 | public String encrypt(String password) { 10 | // Write your logic to encrypt password 11 | return password; 12 | } 13 | 14 | @Override 15 | public String decrypt(String decrypt) { 16 | // Write your logic to decrypt password 17 | return decrypt; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/service/UserPostService.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.service; 2 | 3 | import com.example.jpa.dao.entity.Post; 4 | 5 | import java.util.List; 6 | 7 | public interface UserPostService { 8 | 9 | List getAllPostsByUserId(Long userId); 10 | } 11 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/service/UserPostServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.service; 2 | 3 | import com.example.jpa.dao.entity.Post; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | @Service 13 | public class UserPostServiceImpl implements UserPostService { 14 | 15 | @Autowired 16 | private RestTemplate restTemplate; 17 | 18 | public List getAllPostsByUserId(Long userId) { 19 | Post[] posts = restTemplate.getForObject("https://jsonplaceholder.typicode.com/posts/" + userId, Post[].class); 20 | return posts == null ? Collections.emptyList() : Arrays.asList(posts); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.service; 2 | 3 | import com.example.jpa.dao.entity.User; 4 | import com.example.jpa.model.query.UserQueryModel; 5 | import com.example.jpa.model.request.UserRequestModel; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.Pageable; 8 | 9 | import java.util.Optional; 10 | 11 | public interface UserService { 12 | 13 | Page getAllUsers(UserQueryModel userQueryModel, Pageable pageable); 14 | 15 | Optional getUserById(Long id); 16 | 17 | User createUser(UserRequestModel userRequestModel); 18 | 19 | User updateUser(Long id, UserRequestModel userRequestModel); 20 | 21 | void deleteUserById(Long id); 22 | } 23 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/java/com/example.jpa/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.jpa.service; 2 | 3 | 4 | import com.example.jpa.dao.entity.User; 5 | import com.example.jpa.dao.repository.UserRepository; 6 | import com.example.jpa.dao.spec.UserSpecification; 7 | import com.example.jpa.model.mapper.UserModelMapper; 8 | import com.example.jpa.model.query.UserQueryModel; 9 | import com.example.jpa.model.request.UserRequestModel; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.data.domain.Page; 12 | import org.springframework.data.domain.Pageable; 13 | import org.springframework.stereotype.Service; 14 | 15 | import java.util.Optional; 16 | 17 | @Service 18 | public class UserServiceImpl implements UserService { 19 | 20 | @Autowired 21 | private UserRepository userRepository; 22 | 23 | @Autowired 24 | private UserSpecification userSpecification; 25 | 26 | @Autowired 27 | private UserModelMapper userModelMapper; 28 | 29 | @Override 30 | public Page getAllUsers(UserQueryModel userQueryModel, Pageable pageable) { 31 | return userRepository.findAll(userSpecification.findByUserQueryModel(userQueryModel), pageable); 32 | } 33 | 34 | @Override 35 | public Optional getUserById(Long id) { 36 | return userRepository.findById(id); 37 | } 38 | 39 | @Override 40 | public User createUser(UserRequestModel userRequestModel) { 41 | return userRepository.save(userModelMapper.toUserEntity(userRequestModel)); 42 | 43 | } 44 | 45 | @Override 46 | public User updateUser(Long id, UserRequestModel userRequestModel) { 47 | User user = userModelMapper.toUserEntity(userRequestModel); 48 | user.setId(id); 49 | return userRepository.save(user); 50 | } 51 | 52 | @Override 53 | public void deleteUserById(Long id) { 54 | userRepository.deleteById(id); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /springboot-jpa/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | course.title = "How to use Spring @Value annotation" 2 | course.duration = 30 3 | course.rating = 4.5 4 | course.page_views = 1e+10 5 | course.trending = true 6 | course.created_date = 2020-05-26 7 | course.tags = Spring, Spring Boot, Annotation 8 | course.keyword_count = { 'keyword1': '12', 'keyword2': '44', 'keyword3': '85', 'keyword4': '100'} -------------------------------------------------------------------------------- /springboot-jpa/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #http://localhost:8080/actuator/info 2 | info: 3 | app: 4 | name: spring boot microservice 5 | version: 1.0.0_RELEASE 6 | description: more details about sprint boot microservice 7 | contact-support: apisupportgroup@abc.com 8 | copyright: copyright (c) abc.com 9 | license: MIT 10 | tech-used: 11 | - name: java 12 | version: 11.x 13 | - name: spring-boot 14 | version: 2.x 15 | 16 | #APP SPECIFIC CUSTOM PROPERTIES 17 | app: 18 | name: spring boot application 19 | server: 20 | port: 8080 21 | logging: 22 | level: 23 | root: INFO 24 | com.abc.demo: DEBUG 25 | pattern: 26 | #console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" 27 | console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%8.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %X{REQUEST_ID} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" 28 | spring: 29 | banner: 30 | location: classpath:banner.txt 31 | jackson: 32 | serialization: 33 | indent_output: false 34 | FAIL_ON_EMPTY_BEANS: false 35 | datasource: 36 | type: com.zaxxer.hikari.HikariDataSource 37 | url: jdbc:h2:mem:testdb 38 | username: DB_USER_NAME 39 | password: ENCRYPTED_PASSWORD 40 | hikari: 41 | auto-commit: true 42 | connection-timeout: 30000 43 | idle-timeout: 600000 44 | max-lifetime: 1800000 45 | minimum-idle: 10 46 | maximum-pool-size: 10 47 | # Enabling H2 Console 48 | h2.console.enabled: true 49 | jpa: 50 | generate-ddl: true 51 | show-sql: false 52 | properties: 53 | hibernate: 54 | format_sql: true 55 | logging.level.org.hibernate.SQL: DEBUG 56 | logging.level.org.hibernate.type.descriptor.sql: TRACE -------------------------------------------------------------------------------- /springboot-jpa/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ____ _ _ _ ____ _ 2 | / ___|___ __| (_)_ __ __ _ | \ | | / ___|___ _ __ ___ ___ _ __ | |_ ___ 3 | | | / _ \ / _` | | '_ \ / _` | | \| | | | / _ \| '_ \ / __/ _ \ '_ \| __/ __| 4 | | |__| (_) | (_| | | | | | (_| | | |\ | | |__| (_) | | | | (_| __/ |_) | |_\__ \ 5 | \____\___/ \__,_|_|_| |_|\__, | |_| \_| \____\___/|_| |_|\___\___| .__/ \__|___/ 6 | |___/ |_| -------------------------------------------------------------------------------- /springboot-kafka/.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle ### 2 | .gradle 3 | /build/ 4 | 5 | ### Maven ### 6 | /target/ 7 | /bin/ 8 | !.mvn/wrapper/maven-wrapper.jar 9 | 10 | ### Spring Tool Suite ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBean 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | /out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbnuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | 36 | ### Logs ### 37 | *.logs 38 | 39 | ### Mac ### 40 | *.DS_Store -------------------------------------------------------------------------------- /springboot-kafka/README.md: -------------------------------------------------------------------------------- 1 | # springboot-kafka 2 | Spring Boot Kafka Producer and Consumer Example 3 | -------------------------------------------------------------------------------- /springboot-kafka/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation 'org.springframework.boot:spring-boot-starter-web' 17 | implementation 'org.springframework.kafka:spring-kafka' 18 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 19 | testImplementation 'org.springframework.kafka:spring-kafka-test' 20 | 21 | compileOnly 'org.projectlombok:lombok:1.18.26' 22 | annotationProcessor 'org.projectlombok:lombok:1.18.26' 23 | } 24 | 25 | test { 26 | useJUnitPlatform() 27 | } -------------------------------------------------------------------------------- /springboot-kafka/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.5 9 | 10 | 11 | com.example 12 | demo 13 | 0.0.1-SNAPSHOT 14 | springboot-kafka 15 | Spring Boot Kafka Product and Consumer Examples 16 | 17 | 11 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter 23 | 24 | 25 | org.springframework.kafka 26 | spring-kafka 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | org.springframework.kafka 36 | spring-kafka-test 37 | test 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-maven-plugin 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/KafkaMultiApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class KafkaMultiApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(KafkaMultiApplication.class, "--spring.profiles.active=multi"); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/config/KafkaCustomProperties.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.config; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.apache.kafka.clients.CommonClientConfigs; 6 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.util.CollectionUtils; 10 | 11 | import java.util.*; 12 | 13 | @Configuration 14 | @ConfigurationProperties(prefix = "kafka") 15 | @Getter 16 | @Setter 17 | public class KafkaCustomProperties { 18 | private List bootstrapServers = new ArrayList<>(Collections.singletonList("localhost:9092")); 19 | private String clientId; 20 | private Map properties = new HashMap<>(); 21 | private Map producer; 22 | private Map consumer; 23 | private KafkaProperties.Ssl ssl = new KafkaProperties.Ssl(); 24 | private KafkaProperties.Security security = new KafkaProperties.Security(); 25 | 26 | public Map buildCommonProperties() { 27 | Map properties = new HashMap<>(); 28 | if (this.bootstrapServers != null) { 29 | properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, this.bootstrapServers); 30 | } 31 | if (this.clientId != null) { 32 | properties.put(CommonClientConfigs.CLIENT_ID_CONFIG, this.clientId); 33 | } 34 | properties.putAll(this.ssl.buildProperties()); 35 | properties.putAll(this.security.buildProperties()); 36 | if (!CollectionUtils.isEmpty(this.properties)) { 37 | properties.putAll(this.properties); 38 | } 39 | return properties; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/config/KafkaMultipleConsumerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.config; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 10 | import org.springframework.kafka.core.ConsumerFactory; 11 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import static java.util.Objects.nonNull; 17 | 18 | @Configuration 19 | @RequiredArgsConstructor 20 | @Slf4j 21 | public class KafkaMultipleConsumerConfig { 22 | 23 | private final KafkaCustomProperties kafkaCustomProperties; 24 | 25 | @Bean 26 | @Qualifier("consumer1") 27 | public ConcurrentKafkaListenerContainerFactory consumer1ContainerFactory() { 28 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 29 | factory.setConsumerFactory(consumerFactory("consumer1")); 30 | return factory; 31 | } 32 | 33 | @Bean 34 | @Qualifier("consumer2") 35 | public ConcurrentKafkaListenerContainerFactory consumer2ContainerFactory() { 36 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 37 | factory.setConsumerFactory(consumerFactory("consumer2")); 38 | return factory; 39 | } 40 | 41 | private ConsumerFactory consumerFactory(String consumerName) { 42 | Map properties = new HashMap<>(kafkaCustomProperties.buildCommonProperties()); 43 | if (nonNull(kafkaCustomProperties.getConsumer())) { 44 | KafkaProperties.Consumer consumerProperties = kafkaCustomProperties.getConsumer().get(consumerName); 45 | if (nonNull(consumerProperties)) { 46 | properties.putAll(consumerProperties.buildProperties()); 47 | } 48 | } 49 | log.info("Kafka Consumer '{}' properties: {}", consumerName, properties); 50 | return new DefaultKafkaConsumerFactory<>(properties); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/config/KafkaMultipleProducerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.config; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import org.springframework.kafka.core.ProducerFactory; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import static java.util.Objects.nonNull; 17 | 18 | @Configuration 19 | @RequiredArgsConstructor 20 | @Slf4j 21 | public class KafkaMultipleProducerConfig { 22 | 23 | private final KafkaCustomProperties kafkaCustomProperties; 24 | 25 | @Bean 26 | @Qualifier("producer1") 27 | public KafkaTemplate producer1KafkaTemplate() { 28 | return new KafkaTemplate<>(producerFactory("producer1")); 29 | } 30 | 31 | @Bean 32 | @Qualifier("producer2") 33 | public KafkaTemplate producer2KafkaTemplate() { 34 | return new KafkaTemplate<>(producerFactory("producer2")); 35 | } 36 | 37 | private ProducerFactory producerFactory(String producerName) { 38 | Map properties = new HashMap<>(kafkaCustomProperties.buildCommonProperties()); 39 | if (nonNull(kafkaCustomProperties.getProducer())) { 40 | KafkaProperties.Producer producerProperties = kafkaCustomProperties.getProducer().get(producerName); 41 | if (nonNull(producerProperties)) { 42 | properties.putAll(producerProperties.buildProperties()); 43 | } 44 | } 45 | log.info("Kafka Producer '{}' properties: {}", producerName, properties); 46 | return new DefaultKafkaProducerFactory<>(properties); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/service/KafkaConsumerService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.service; 2 | 3 | public interface KafkaConsumerService { 4 | 5 | void receive(String message); 6 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/service/KafkaFirstConsumerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.service; 2 | 3 | import com.example.kafka.single.service.KafkaConsumerService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.kafka.annotation.KafkaListener; 8 | import org.springframework.messaging.handler.annotation.Payload; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | @Slf4j 13 | public class KafkaFirstConsumerServiceImpl implements KafkaConsumerService { 14 | 15 | @KafkaListener(topics = {"${kafka.consumer.consumer1.topic}"}, groupId = "${kafka.consumer.consumer1.group-id}", containerFactory = "consumer1ContainerFactory") 16 | public void receive(@Payload String message) { 17 | log.info("message received in consumer1: {}", message); 18 | } 19 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/service/KafkaFirstProducerService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.kafka.core.KafkaTemplate; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | @Slf4j 12 | public class KafkaFirstProducerService implements KafkaProducerService { 13 | @Qualifier("producer1") 14 | private KafkaTemplate kafkaTemplate; 15 | 16 | @Value("${kafka.producer.producer1.topic}") 17 | private String topic; 18 | 19 | @Override 20 | public void send(String message) { 21 | log.info("sending message from first producer: {}", message); 22 | kafkaTemplate.send(topic, message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/service/KafkaProducerService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.service; 2 | 3 | public interface KafkaProducerService { 4 | 5 | void send(String message); 6 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/service/KafkaSecondConsumerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.service; 2 | 3 | import com.example.kafka.single.service.KafkaConsumerService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.kafka.annotation.KafkaListener; 6 | import org.springframework.messaging.handler.annotation.Payload; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | @Slf4j 11 | public class KafkaSecondConsumerServiceImpl implements KafkaConsumerService { 12 | 13 | @KafkaListener(topics = {"${kafka.consumer.consumer2.topic}"}, groupId = "${kafka.consumer.consumer2.group-id}", containerFactory = "consumer2ContainerFactory") 14 | public void receive(@Payload String message) { 15 | log.info("message received in consumer2: {}", message); 16 | } 17 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/multi/service/KafkaSecondProducerService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.multi.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Qualifier; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.kafka.core.KafkaTemplate; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | @Slf4j 11 | public class KafkaSecondProducerService { 12 | @Qualifier("producer2") 13 | private KafkaTemplate kafkaTemplate; 14 | 15 | @Value("${kafka.producer.producer2.topic}") 16 | private String topic; 17 | 18 | public void send(String message) { 19 | log.info("sending message from second producer: {}", message); 20 | kafkaTemplate.send(topic, message); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/KafkaApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class KafkaApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(KafkaApplication.class, args); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/config/KafkaConsumerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.config; 2 | 3 | import com.example.kafka.single.service.CryptoService; 4 | import org.apache.kafka.common.config.SslConfigs; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.kafka.annotation.EnableKafka; 9 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 10 | import org.springframework.kafka.core.ConsumerFactory; 11 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @EnableKafka 17 | @Configuration 18 | public class KafkaConsumerConfig { 19 | 20 | @Autowired 21 | private ConsumerFactory consumerFactory; 22 | 23 | @Autowired 24 | private CryptoService cryptoService; 25 | 26 | @Bean 27 | public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { 28 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 29 | factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfig())); 30 | return factory; 31 | } 32 | 33 | private Map consumerConfig() { 34 | Map consumerConfig = new HashMap<>(consumerFactory.getConfigurationProperties()); 35 | decryptAndAddToConsumerConfig(consumerConfig, SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG); 36 | decryptAndAddToConsumerConfig(consumerConfig, SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG); 37 | decryptAndAddToConsumerConfig(consumerConfig, SslConfigs.SSL_KEY_PASSWORD_CONFIG); 38 | return consumerConfig; 39 | } 40 | 41 | private void decryptAndAddToConsumerConfig(Map config, String property) { 42 | config.compute(property, (k, v) -> cryptoService.decrypt((String) v)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/config/KafkaProducerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.config; 2 | 3 | import com.example.kafka.single.service.CryptoService; 4 | import org.apache.kafka.common.config.SslConfigs; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 9 | import org.springframework.kafka.core.KafkaTemplate; 10 | import org.springframework.kafka.core.ProducerFactory; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | @Configuration 16 | public class KafkaProducerConfig { 17 | 18 | @Autowired 19 | private ProducerFactory producerFactory; 20 | 21 | @Autowired 22 | private CryptoService cryptoService; 23 | 24 | public Map producerConfig() { 25 | Map producerConfig = new HashMap<>(producerFactory.getConfigurationProperties()); 26 | decryptAndAddToConsumerConfig(producerConfig, SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG); 27 | decryptAndAddToConsumerConfig(producerConfig, SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG); 28 | decryptAndAddToConsumerConfig(producerConfig, SslConfigs.SSL_KEY_PASSWORD_CONFIG); 29 | return producerConfig; 30 | } 31 | 32 | @Bean 33 | public KafkaTemplate kafkaTemplate() { 34 | return new KafkaTemplate<>(new DefaultKafkaProducerFactory<>(producerConfig())); 35 | } 36 | 37 | private void decryptAndAddToConsumerConfig(Map config, String property) { 38 | config.compute(property, (k, v) -> cryptoService.decrypt((String) v)); 39 | } 40 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/controller/KafkaController.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.controller; 2 | 3 | import com.example.kafka.single.service.KafkaProducerService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | @RequestMapping("/kafka") 12 | public class KafkaController { 13 | 14 | @Autowired 15 | private KafkaProducerService kafkaProducerService; 16 | 17 | @PostMapping("/publish") 18 | public void sendMessageToKafkaTopic(@RequestParam("message") String message) { 19 | kafkaProducerService.send(message); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/service/CryptoService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.service; 2 | 3 | public interface CryptoService { 4 | 5 | public String encrypt(String password); 6 | 7 | public String decrypt(String password); 8 | } 9 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/service/CryptoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class CryptoServiceImpl implements CryptoService { 7 | 8 | @Override 9 | public String encrypt(String password) { 10 | // Write your logic to encrypt password 11 | return password; 12 | } 13 | 14 | @Override 15 | public String decrypt(String decrypt) { 16 | // Write your logic to decrypt password 17 | return decrypt; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/service/KafkaConsumerService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.service; 2 | 3 | public interface KafkaConsumerService { 4 | 5 | void receive(String message); 6 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/service/KafkaConsumerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.service; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.kafka.annotation.KafkaListener; 6 | import org.springframework.messaging.handler.annotation.Payload; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class KafkaConsumerServiceImpl implements KafkaConsumerService { 11 | 12 | private static final Logger logger = LoggerFactory.getLogger(KafkaConsumerServiceImpl.class); 13 | 14 | @KafkaListener(topics = {"#{'${app.kafka.consumer.topic}'.split(',')}"}) 15 | public void receive(@Payload String message) { 16 | logger.info("message received: {}", message); 17 | } 18 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/service/KafkaProducerService.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.service; 2 | 3 | public interface KafkaProducerService { 4 | 5 | void send(String message); 6 | } -------------------------------------------------------------------------------- /springboot-kafka/src/main/java/com/example/kafka/single/service/KafkaProducerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.single.service; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.kafka.core.KafkaTemplate; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | public class KafkaProducerServiceImpl implements KafkaProducerService { 12 | 13 | private static final Logger logger = LoggerFactory.getLogger(KafkaProducerServiceImpl.class); 14 | 15 | @Autowired 16 | private KafkaTemplate kafkaTemplate; 17 | 18 | @Value("${app.kafka.producer.topic}") 19 | private String topic; 20 | 21 | @Override 22 | public void send(String message) { 23 | logger.info("message sent: {}", message); 24 | kafkaTemplate.send(topic, message); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/resources/app/store/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/springboot-kafka/src/main/resources/app/store/keystore.jks -------------------------------------------------------------------------------- /springboot-kafka/src/main/resources/app/store/truststore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/springboot-kafka/src/main/resources/app/store/truststore.jks -------------------------------------------------------------------------------- /springboot-kafka/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | #APP SPECIFIC CUSTOM PROPERTIES 2 | app: 3 | kafka: 4 | producer: 5 | topic: test 6 | consumer: 7 | topic: test 8 | #SPRING PROPERTIES 9 | spring: 10 | kafka: 11 | bootstrap-servers: localhost:9092 12 | ssl: 13 | protocol: SSL 14 | trust-store-location: classpath:/app/store/truststore.jks 15 | trust-store-password: password 16 | key-store-location: classpath:/app/store/keystore.jks 17 | key-store-password: password 18 | key-password: password 19 | producer: 20 | retries: 0 21 | acks: all 22 | key-serializer: org.apache.kafka.common.serialization.StringSerializer 23 | value-serializer: org.apache.kafka.common.serialization.StringSerializer 24 | consumer: 25 | group-id: group-1 26 | auto-offset-reset: earliest 27 | key-deserializer: org.apache.kafka.common.serialization.StringDeserializer 28 | value-deserializer: org.apache.kafka.common.serialization.StringDeserializer 29 | 30 | #LOGGING PROPERTIES 31 | logging: 32 | level: 33 | root: INFO -------------------------------------------------------------------------------- /springboot-kafka/src/main/resources/application-multi.yml: -------------------------------------------------------------------------------- 1 | #APP SPECIFIC CUSTOM PROPERTIES 2 | app: 3 | kafka: 4 | producer: 5 | topic: test 6 | consumer: 7 | topic: test 8 | 9 | #Kafka PROPERTIES 10 | kafka: 11 | bootstrap-servers: localhost:9092 12 | ssl: 13 | protocol: SSL 14 | trust-store-location: classpath:/app/store/truststore.jks 15 | trust-store-password: password 16 | key-store-location: classpath:/app/store/keystore.jks 17 | key-store-password: password 18 | key-password: password 19 | producer: 20 | producer1: 21 | bootstrap-servers: server1:9092, server1:9093 22 | topic: topic1 23 | retries: 0 24 | acks: all 25 | key-serializer: org.apache.kafka.common.serialization.StringSerializer 26 | value-serializer: org.apache.kafka.common.serialization.StringSerializer 27 | producer2: 28 | bootstrap-servers: server2:9092, server2:9093 29 | topic: topic2 30 | retries: 2 31 | acks: 1 32 | key-serializer: org.apache.kafka.common.serialization.StringSerializer 33 | value-serializer: org.apache.kafka.common.serialization.StringSerializer 34 | consumer: 35 | consumer1: 36 | topic: topic1 37 | group-id: group1 38 | auto-offset-reset: earliest 39 | key-deserializer: org.apache.kafka.common.serialization.StringDeserializer 40 | value-deserializer: org.apache.kafka.common.serialization.StringDeserializer 41 | consumer2: 42 | topic: topic2 43 | group-id: group2 44 | auto-offset-reset: latest 45 | key-deserializer: org.apache.kafka.common.serialization.StringDeserializer 46 | value-deserializer: org.apache.kafka.common.serialization.StringDeserializer 47 | 48 | #LOGGING PROPERTIES 49 | logging: 50 | level: 51 | root: INFO -------------------------------------------------------------------------------- /springboot-kafka/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | #APP SPECIFIC CUSTOM PROPERTIES 2 | app: 3 | kafka: 4 | producer: 5 | topic: 6 | consumer: 7 | topic: 8 | #SPRING PROPERTIES 9 | spring: 10 | profiles: 11 | active: local 12 | kafka: 13 | bootstrap-servers: :,:,: 14 | properties: 15 | #Server host name verification is disabled by setting ssl.endpoint.identification.algorithm to an empty string 16 | ssl.endpoint.identification.algorithm: 17 | ssl: 18 | protocol: SSL 19 | trust-store-location: file:/app/store/truststore.jks 20 | trust-store-password: 21 | key-store-location: file:/app/store/keystore.jks 22 | key-store-password: 23 | key-password: 24 | producer: 25 | bootstrap-servers: 26 | retries: 0 27 | acks: all 28 | key-serializer: org.apache.kafka.common.serialization.StringSerializer 29 | value-serializer: org.apache.kafka.common.serialization.StringSerializer 30 | consumer: 31 | bootstrap-servers: localhost:9200,localhost:9300,localhost:9400 32 | group-id: 33 | auto-offset-reset: earliest 34 | key-deserializer: org.apache.kafka.common.serialization.StringDeserializer 35 | value-deserializer: org.apache.kafka.common.serialization.StringDeserializer 36 | -------------------------------------------------------------------------------- /springboot-kafka/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #SPRING PROPERTIES 2 | spring: 3 | profiles: 4 | active: local 5 | -------------------------------------------------------------------------------- /springboot-kafka/src/test/java/com/example/kafka/KafkaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | 7 | @SpringBootTest 8 | public class KafkaApplicationTests { 9 | 10 | @Test 11 | public void contextLoads() { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /springboot-kafka/src/test/java/com/example/kafka/service/KafkaConsumerServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.service; 2 | 3 | import java.time.Duration; 4 | import java.util.Collections; 5 | import java.util.Properties; 6 | import java.util.concurrent.ExecutionException; 7 | 8 | import org.apache.kafka.clients.consumer.ConsumerConfig; 9 | import org.apache.kafka.clients.consumer.ConsumerRecord; 10 | import org.apache.kafka.clients.consumer.ConsumerRecords; 11 | import org.apache.kafka.clients.consumer.KafkaConsumer; 12 | import org.apache.kafka.common.serialization.StringDeserializer; 13 | 14 | public class KafkaConsumerServiceTest { 15 | 16 | public static void main(String[] args) throws InterruptedException, ExecutionException{ 17 | //Create consumer property 18 | String bootstrapServer = "localhost:9092"; 19 | String groupId = "my-first-consumer-group"; 20 | String topicName = "my-first-topic"; 21 | 22 | Properties properties = new Properties(); 23 | properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer); 24 | properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); 25 | properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); 26 | properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, groupId); 27 | properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); 28 | properties.setProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); 29 | 30 | //Create consumer 31 | KafkaConsumer consumer = new KafkaConsumer<>(properties); 32 | 33 | //Subscribe consumer to topic(s) 34 | consumer.subscribe(Collections.singleton(topicName)); 35 | 36 | //Poll for new data 37 | while(true){ 38 | ConsumerRecords records = consumer.poll(Duration.ofMillis(1000)); 39 | 40 | for(ConsumerRecord record: records){ 41 | System.out.println(record.key() + record.value()); 42 | System.out.println(record.topic() + record.partition() + record.offset()); 43 | } 44 | 45 | //Commit consumer offset manually (recommended) 46 | consumer.commitAsync(); 47 | } 48 | 49 | } 50 | } -------------------------------------------------------------------------------- /springboot-kafka/src/test/java/com/example/kafka/service/KafkaProducerServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.kafka.service; 2 | 3 | import java.util.Properties; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | import org.apache.kafka.clients.producer.KafkaProducer; 7 | import org.apache.kafka.clients.producer.ProducerConfig; 8 | import org.apache.kafka.clients.producer.ProducerRecord; 9 | import org.apache.kafka.common.serialization.StringSerializer; 10 | 11 | public class KafkaProducerServiceTest { 12 | 13 | public static void main(String[] args) throws InterruptedException, ExecutionException{ 14 | //Create producer property 15 | String bootstrapServer = "localhost:9092"; 16 | Properties properties = new Properties(); 17 | properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer); 18 | properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); 19 | properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); 20 | 21 | //Create safe producer 22 | properties.setProperty(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true"); 23 | properties.setProperty(ProducerConfig.ACKS_CONFIG, "all"); 24 | properties.setProperty(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, "5"); 25 | properties.setProperty(ProducerConfig.RETRIES_CONFIG, Integer.toString(Integer.MAX_VALUE)); 26 | 27 | //High throughput producer (at the expense of a bit of latency and CPU usage) 28 | properties.setProperty(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy"); 29 | properties.setProperty(ProducerConfig.LINGER_MS_CONFIG, "20"); //20ms wait time 30 | properties.setProperty(ProducerConfig.BATCH_SIZE_CONFIG, Integer.toString(32*1024)); //32KB batch size 31 | 32 | //Create producer 33 | KafkaProducer producer = new KafkaProducer<>(properties); 34 | 35 | //create a producer record 36 | ProducerRecord record = new ProducerRecord<>("topicName", "firstRecord"); 37 | //create producer record with key 38 | //new ProducerRecord<>("topicName", "MessageKey", "Message"); 39 | //create producer record with key and partition number 40 | //new ProducerRecord<>("topicName", 1 /*partition number*/, "MessageKey", "Message"); 41 | 42 | //send data - asynchronous 43 | //without callback 44 | //producer.send(record); 45 | //with callback 46 | producer.send(record, (recordMetadata, exception) -> { 47 | if(exception == null){ 48 | System.out.println(recordMetadata.topic() + "+" + recordMetadata.partition() + "+" + recordMetadata.offset()); 49 | }else{ 50 | System.err.println(exception.getMessage()); 51 | } 52 | }); 53 | 54 | //send data - synchronous 55 | //without callback 56 | //producer.send(record).get(); //.get() make it synchronous call 57 | 58 | //flush data 59 | producer.flush(); 60 | 61 | //flush and close producer 62 | producer.close(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /springboot-openfeign/.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle ### 2 | .gradle 3 | /build/ 4 | 5 | ### Maven ### 6 | /target/ 7 | /bin/ 8 | !.mvn/wrapper/maven-wrapper.jar 9 | 10 | ### Spring Tool Suite ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBean 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | /out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbnuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | 36 | ### Logs ### 37 | *.logs 38 | 39 | ### Mac ### 40 | *.DS_Store -------------------------------------------------------------------------------- /springboot-openfeign/README.md: -------------------------------------------------------------------------------- 1 | # springboot-openfeign 2 | 3 | ## Overview 4 | Consume RESTFul APIs using OpenFeign Web Client in Spring Boot 5 | 6 | ## OpenFeign Example Details 7 | 1. **Build RESTFul APIs** using `@RestController` and use of short annotations `@GetMapping`, `@PostMapping`, `@PutMapping`, and `@DeleteMapping` 8 | 2. **Service Layer** development using `@Service` to provide business logic to `@RestController` 9 | 6. **Consume RESTFul APIs** provided by other services using `@FeignClient` 10 | 7. **Boilerplate Code** generation using `Lombok` annotations such as `@Data`, `@Value`, `@ToString`, `@Getters`, and `@Setters` 11 | 8. **Unit and Integration Tests** for FeignClient, Controller and Service Layer -------------------------------------------------------------------------------- /springboot-openfeign/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | ext { 16 | set('springCloudVersion', "2020.0.3") 17 | } 18 | 19 | dependencies { 20 | implementation 'org.springframework.boot:spring-boot-starter-web' 21 | implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' 22 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.9.RELEASE' 23 | implementation 'io.github.openfeign:feign-hc5' 24 | implementation 'io.github.openfeign.form:feign-form:3.8.0' 25 | implementation 'org.springdoc:springdoc-openapi-ui:latest.release' 26 | implementation 'commons-io:commons-io:2.7' 27 | 28 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 29 | testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner' 30 | 31 | compileOnly 'org.projectlombok:lombok:1.18.20' 32 | annotationProcessor 'org.projectlombok:lombok:1.18.20' 33 | } 34 | 35 | dependencyManagement { 36 | imports { 37 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 38 | } 39 | } 40 | 41 | test { 42 | useJUnitPlatform() 43 | } -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/OpenFeignApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.openfeign.EnableFeignClients; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.web.client.RestTemplate; 8 | 9 | @SpringBootApplication 10 | @EnableFeignClients 11 | public class OpenFeignApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(OpenFeignApplication.class, args); 15 | } 16 | 17 | @Bean 18 | public RestTemplate getRestTemplate() { 19 | return new RestTemplate(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/client/UserFeignClient.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.client; 2 | 3 | import com.example.openfeign.config.ApacheHttp5FeignSslClientConfig; 4 | import com.example.openfeign.model.UserRequest; 5 | import com.example.openfeign.model.ListUserResponse; 6 | import com.example.openfeign.model.SingleUserResponse; 7 | import com.example.openfeign.model.User; 8 | import org.springframework.cloud.openfeign.FeignClient; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | @FeignClient(name = "userFeignClient", 12 | url = "${client.api.baseUrl}", 13 | configuration = ApacheHttp5FeignSslClientConfig.class, 14 | fallback = UserFeignClientFallback.class) 15 | public interface UserFeignClient { 16 | 17 | @GetMapping("/api/users") 18 | ListUserResponse getUserList(@RequestParam("page") Integer page); 19 | 20 | @GetMapping("/api/users") 21 | ListUserResponse getUserListDelayed(@RequestParam("page") Integer page, @RequestParam("delay") Integer delay); 22 | 23 | @GetMapping("/api/users/{userId}") 24 | SingleUserResponse getUserById(@PathVariable("userId") Long userId); 25 | 26 | @PostMapping("/api/users") 27 | User createUser(@RequestBody UserRequest userRequest); 28 | 29 | @PutMapping("/api/users") 30 | User updateUser(@RequestBody UserRequest userRequest); 31 | 32 | @DeleteMapping("/api/users/{userId}") 33 | void deleteUserById(@PathVariable("userId") Long userId); 34 | } 35 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/client/UserFeignClientFallback.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.client; 2 | 3 | import com.example.openfeign.model.ListUserResponse; 4 | import com.example.openfeign.model.SingleUserResponse; 5 | import com.example.openfeign.model.User; 6 | import com.example.openfeign.model.UserRequest; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.List; 10 | 11 | @Component 12 | public class UserFeignClientFallback implements UserFeignClient { 13 | 14 | @Override 15 | public ListUserResponse getUserList(Integer page) { 16 | return null; 17 | } 18 | 19 | @Override 20 | public ListUserResponse getUserListDelayed(Integer page, Integer delay) { 21 | ListUserResponse listUserResponse = new ListUserResponse(); 22 | listUserResponse.setUsers(List.of(User.builder() 23 | .id(1L) 24 | .firstName("George") 25 | .lastName("Bluth") 26 | .build())); 27 | return listUserResponse; 28 | } 29 | 30 | @Override 31 | public SingleUserResponse getUserById(Long userId) { 32 | return null; 33 | } 34 | 35 | @Override 36 | public User createUser(UserRequest userRequest) { 37 | return null; 38 | } 39 | 40 | @Override 41 | public User updateUser(UserRequest userRequest) { 42 | return null; 43 | } 44 | 45 | @Override 46 | public void deleteUserById(Long userId) { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/config/FeignClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.api.config; 2 | 3 | import feign.RequestInterceptor; 4 | import feign.auth.BasicAuthRequestInterceptor; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | public class FeignClientConfig { 8 | 9 | /** 10 | * Enable this bean if you want to add headers in HTTP request 11 | */ 12 | @Bean 13 | public RequestInterceptor requestInterceptor() { 14 | return requestTemplate -> { 15 | requestTemplate.header("Content-Type", "application/json"); 16 | requestTemplate.header("Accept", "application/json"); 17 | requestTemplate.header("header_1", "value_1"); 18 | requestTemplate.header("header_2", "value_2"); 19 | requestTemplate.header("header_3", "value_3"); 20 | }; 21 | } 22 | 23 | /** 24 | * Enable this bean if you want to add basic Authorization header 25 | * for e.g. Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= 26 | */ 27 | @Bean 28 | public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { 29 | return new BasicAuthRequestInterceptor("username", "password"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.controller; 2 | 3 | import com.example.openfeign.model.User; 4 | import com.example.openfeign.model.UserRequest; 5 | import com.example.openfeign.service.UserService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | 11 | @RestController 12 | @RequiredArgsConstructor 13 | @RequestMapping("/users") 14 | public class UserController { 15 | 16 | private final UserService userService; 17 | 18 | @GetMapping 19 | List getUserListDelayed(@RequestParam("page") Integer page, @RequestParam(name = "delay", defaultValue = "0") Integer delay){ 20 | return userService.getUserListDelayed(page, delay); 21 | } 22 | 23 | @GetMapping("/{userId}") 24 | User getUserById(@PathVariable("userId") Long userId){ 25 | return userService.getUserById(userId); 26 | } 27 | 28 | @PostMapping 29 | User createUser(@RequestBody UserRequest userRequest){ 30 | return userService.createUser(userRequest); 31 | } 32 | 33 | @PutMapping 34 | User updateUser(@RequestBody UserRequest userRequest){ 35 | return userService.updateUser(userRequest); 36 | } 37 | 38 | @DeleteMapping("/{userId}") 39 | void deleteUserById(@PathVariable("userId") Long userId){ 40 | userService.deleteUserById(userId); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/model/ListUserResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | public class ListUserResponse { 10 | Integer page; 11 | 12 | @JsonProperty("per_page") 13 | Integer perPage; 14 | 15 | Long total; 16 | 17 | @JsonProperty("total_pages") 18 | Integer totalPages; 19 | 20 | @JsonProperty("data") 21 | List users; 22 | } 23 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/model/SingleUserResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class SingleUserResponse { 8 | 9 | @JsonProperty("data") 10 | User user; 11 | } 12 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Builder 9 | public class User { 10 | Long id; 11 | 12 | String email; 13 | 14 | @JsonProperty("first_name") 15 | String firstName; 16 | 17 | @JsonProperty("last_name") 18 | String lastName; 19 | 20 | String avatar; 21 | 22 | String name; 23 | 24 | String job; 25 | 26 | String createdAt; 27 | 28 | String updatedAt; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/model/UserRequest.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class UserRequest { 7 | String name; 8 | String job; 9 | } 10 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/java/com/example/openfeign/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.service; 2 | 3 | import com.example.openfeign.client.UserFeignClient; 4 | import com.example.openfeign.model.User; 5 | import com.example.openfeign.model.UserRequest; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.web.bind.annotation.DeleteMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.PutMapping; 12 | 13 | import java.util.List; 14 | 15 | @Service 16 | @RequiredArgsConstructor 17 | public class UserService { 18 | private final UserFeignClient userFeignClient; 19 | 20 | public List getUserList(Integer page) { 21 | return userFeignClient.getUserList(page).getUsers(); 22 | } 23 | 24 | public List getUserListDelayed(Integer page, Integer delay) { 25 | return userFeignClient.getUserListDelayed(page, delay).getUsers(); 26 | } 27 | 28 | public User getUserById(Long userId) { 29 | return userFeignClient.getUserById(userId).getUser(); 30 | } 31 | 32 | public User createUser(UserRequest userRequest){ 33 | return userFeignClient.createUser(userRequest); 34 | } 35 | 36 | public User updateUser(UserRequest userRequest){ 37 | return userFeignClient.updateUser(userRequest); 38 | } 39 | 40 | public void deleteUserById(Long userId){ 41 | userFeignClient.deleteUserById(userId); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /springboot-openfeign/src/main/resources/KeyStore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/springboot-openfeign/src/main/resources/KeyStore.jks -------------------------------------------------------------------------------- /springboot-openfeign/src/main/resources/TrustStore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/springboot-openfeign/src/main/resources/TrustStore.jks -------------------------------------------------------------------------------- /springboot-openfeign/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #APP SPECIFIC CUSTOM PROPERTIES 2 | 3 | server: 4 | port: 8080 5 | 6 | logging: 7 | level: 8 | root: INFO 9 | com.example.openfeign.client: DEBUG 10 | pattern: 11 | #console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" 12 | console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%8.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %X{REQUEST_ID} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" 13 | 14 | feign: 15 | circuitbreaker: 16 | enabled: true 17 | client: 18 | config: 19 | default: 20 | connectTimeout: 5000 21 | readTimeout: 5000 22 | loggerLevel: BASIC 23 | userFeignClient: 24 | loggerLevel: FULL 25 | 26 | client: 27 | api: 28 | baseUrl: https://reqres.in 29 | ssl: 30 | protocol: TLS 31 | key-store-type: JKS 32 | key-store: classpath:KeyStore.jks 33 | key-store-password: changeit 34 | key-password: changeit 35 | trust-store: classpath:TrustStore.jks 36 | trust-store-password: changeit 37 | proxy-host: 38 | proxy-port: -------------------------------------------------------------------------------- /springboot-openfeign/src/test/java/com/example/openfeign/OpenFeignApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign; 2 | 3 | import com.example.openfeign.controller.UserController; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @SpringBootTest 11 | class OpenFeignApplicationTests { 12 | 13 | @Autowired 14 | private UserController userController; 15 | 16 | @Test 17 | void contextLoads() { 18 | assertThat(userController).isNotNull(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /springboot-openfeign/src/test/java/com/example/openfeign/client/UserFeignClientTest.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.client; 2 | 3 | import com.example.openfeign.model.SingleUserResponse; 4 | import com.example.openfeign.model.User; 5 | import org.apache.commons.io.IOUtils; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; 10 | import org.springframework.core.io.ClassPathResource; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.test.context.ActiveProfiles; 14 | 15 | import java.io.IOException; 16 | import java.nio.charset.StandardCharsets; 17 | 18 | import static com.github.tomakehurst.wiremock.client.WireMock.*; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 22 | properties = { 23 | "client.api.baseUrl=http://localhost:" + UserFeignClientTest.port 24 | }) 25 | @AutoConfigureWireMock(port = UserFeignClientTest.port) 26 | public class UserFeignClientTest { 27 | 28 | @Autowired 29 | UserFeignClient userFeignClient; 30 | 31 | public static final int port = 9091; 32 | 33 | @Test 34 | public void getUserById_whenValidClient_returnValidResponse() throws Exception { 35 | // Using WireMock to mock client API: 36 | stubFor(get(urlEqualTo("/api/users/1")) 37 | .willReturn(aResponse() 38 | .withStatus(HttpStatus.OK.value()) 39 | .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) 40 | .withBody(read("stubs/user.json")))); 41 | 42 | SingleUserResponse userData = userFeignClient.getUserById(1L); 43 | User user = userData.getUser(); 44 | 45 | // We're asserting if WireMock responded properly 46 | assertThat(user.getId()).isEqualTo(1); 47 | assertThat(user.getFirstName()).isEqualTo("George"); 48 | assertThat(user.getLastName()).isEqualTo("Bluth"); 49 | } 50 | 51 | private String read(String location) throws IOException { 52 | return IOUtils.toString(new ClassPathResource(location).getInputStream(), StandardCharsets.UTF_8); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /springboot-openfeign/src/test/java/com/example/openfeign/controller/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.controller; 2 | 3 | import com.example.openfeign.domain.UserTestData; 4 | import com.example.openfeign.service.UserService; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.mock.mockito.MockBean; 10 | import org.springframework.test.web.servlet.MockMvc; 11 | 12 | import static org.hamcrest.Matchers.equalTo; 13 | import static org.mockito.ArgumentMatchers.anyLong; 14 | import static org.mockito.Mockito.when; 15 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 16 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 17 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 18 | 19 | @SpringBootTest 20 | @AutoConfigureMockMvc 21 | public class UserControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private UserService userService; 28 | 29 | @Test 30 | public void getUserById_whenValidUserId_returnThatUser() throws Exception { 31 | when(userService.getUserById(anyLong())).thenReturn(UserTestData.user()); 32 | 33 | mockMvc.perform(get("/users/1")) 34 | .andExpect(status().is2xxSuccessful()) 35 | .andExpect(jsonPath("$.id", equalTo(1))) 36 | .andExpect(jsonPath("$.first_name", equalTo("George"))) 37 | .andExpect(jsonPath("$.last_name", equalTo("Bluth"))); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /springboot-openfeign/src/test/java/com/example/openfeign/domain/UserTestData.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.domain; 2 | 3 | import com.example.openfeign.model.User; 4 | import com.example.openfeign.model.SingleUserResponse; 5 | 6 | import java.util.List; 7 | 8 | public class UserTestData { 9 | 10 | 11 | public static User user() { 12 | return User.builder() 13 | .id(1L) 14 | .firstName("George") 15 | .lastName("Bluth") 16 | .build(); 17 | } 18 | 19 | public static SingleUserResponse userData() { 20 | SingleUserResponse singleUserResponse = new SingleUserResponse(); 21 | singleUserResponse.setUser(user()); 22 | return singleUserResponse; 23 | } 24 | 25 | public static List users() { 26 | return List.of(user()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springboot-openfeign/src/test/java/com/example/openfeign/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.openfeign.service; 2 | 3 | import com.example.openfeign.client.UserFeignClient; 4 | import com.example.openfeign.domain.UserTestData; 5 | import com.example.openfeign.model.User; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.mock.mockito.MockBean; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | import static org.mockito.ArgumentMatchers.anyLong; 13 | import static org.mockito.Mockito.when; 14 | 15 | @SpringBootTest 16 | public class UserServiceTest { 17 | 18 | @MockBean 19 | private UserFeignClient userFeignClient; 20 | 21 | @Autowired 22 | private UserService userService; 23 | 24 | @Test 25 | public void getUserById_whenValidUserId_returnThatUser() { 26 | when(userFeignClient.getUserById(anyLong())).thenReturn(UserTestData.userData()); 27 | 28 | User user = userService.getUserById(1L); 29 | 30 | assertThat(user.getId()).isEqualTo(1); 31 | assertThat(user.getFirstName()).isEqualTo("George"); 32 | assertThat(user.getLastName()).isEqualTo("Bluth"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /springboot-openfeign/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | client: 2 | post: 3 | baseUrl: http://localhost:9091 -------------------------------------------------------------------------------- /springboot-openfeign/src/test/resources/stubs/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "id": 1, 4 | "email": "george.bluth@reqres.in", 5 | "first_name": "George", 6 | "last_name": "Bluth", 7 | "avatar": "https://reqres.in/img/faces/1-image.jpg" 8 | }, 9 | "support": { 10 | "url": "https://reqres.in/#support-heading", 11 | "text": "To keep ReqRes free, contributions towards server costs are appreciated!" 12 | } 13 | } -------------------------------------------------------------------------------- /springboot-xml/.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle ### 2 | .gradle 3 | /build/ 4 | 5 | ### Maven ### 6 | /target/ 7 | /bin/ 8 | !.mvn/wrapper/maven-wrapper.jar 9 | 10 | ### Spring Tool Suite ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBean 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | /out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbnuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | 36 | ### Logs ### 37 | *.logs 38 | 39 | ### Mac ### 40 | *.DS_Store -------------------------------------------------------------------------------- /springboot-xml/README.md: -------------------------------------------------------------------------------- 1 | # springboot-xml 2 | 3 | ## Overview 4 | Consider a use case, where you are given an XML Schema `.xsd` file, based on which you should convert an XML to JSON format. There is no straightforward solution for this. We need to do following:- 5 | 6 | 1. First we need to generate Java class files from XSD schema `.xsd` file 7 | 2. Use Jackson for deserialization (XML to Java Object) and serialization (Java Object to JSON), which result into XML to JSON conversion. 8 | 9 | We want to automate this as much as possible so that if there is any update in XML schema, it can be adapted with minimal change. We will create a JAXB task (gradle or maven) here, which is tied with project build phase and responsible for generating Java class files from given schema `.xsd` file. 10 | 11 | When we converted the XML to JSON file using the generated Java class files, we find mainly two issues which were not meeting our requirement and solved them:- 12 | 13 | 1. Date and Time in XSD `xs:date` and `xs:time` are converted to timestamp format instead of date and time format 14 | 2. Enum value in XSD `` having space is converted to "HALF_UP" instead of "half up" 15 | 16 | ## Explanation 17 | Follow the post https://codingnconcepts.com/java/convert-xsd-to-json-using-jaxb/ for detailed explanation -------------------------------------------------------------------------------- /springboot-xml/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | ext { 16 | set('springCloudVersion', "2020.0.3") 17 | } 18 | 19 | dependencies { 20 | implementation 'org.springframework.boot:spring-boot-starter-web' 21 | implementation 'commons-io:commons-io:2.6' 22 | implementation 'javax.xml.bind:jaxb-api:2.3.1' 23 | implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.12.3' 24 | implementation 'org.jvnet.jaxb2_commons:jaxb2-basics-runtime:0.12.0' 25 | 26 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 27 | testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner' 28 | 29 | compileOnly 'org.projectlombok:lombok:1.18.20' 30 | annotationProcessor 'org.projectlombok:lombok:1.18.20' 31 | } 32 | 33 | dependencyManagement { 34 | imports { 35 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 36 | } 37 | } 38 | 39 | test { 40 | useJUnitPlatform() 41 | } 42 | 43 | configurations { 44 | jaxb 45 | } 46 | 47 | // Dependencies to be used by "jaxb" task 48 | dependencies { 49 | jaxb( 50 | 'com.sun.xml.bind:jaxb-xjc:2.3.1', 51 | 'com.sun.xml.bind:jaxb-impl:2.3.1', 52 | 'org.glassfish.jaxb:jaxb-runtime:2.3.1', 53 | 'org.jvnet.jaxb2_commons:jaxb2-basics:0.12.0' 54 | ) 55 | } 56 | 57 | // JAXB task definition 58 | task jaxb { 59 | def generatedResouces = "src/main/generated-sources" 60 | def jaxbTargetDir = file(generatedResouces) 61 | jaxbTargetDir.deleteDir() 62 | 63 | doLast { 64 | jaxbTargetDir.mkdirs() 65 | ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath) 66 | ant.jaxbTargetDir = jaxbTargetDir 67 | ant.xjc(destDir: '${jaxbTargetDir}', package: 'com.example.jaxb', extension: true){ 68 | schema(dir: "src/main/resources/schema", includes: "schema.xsd") 69 | binding(dir: "src/main/resources/jaxb", includes: "bindings.xjb") 70 | arg(line: '-XenumValue') 71 | } 72 | } 73 | } 74 | 75 | // Add generated classes directory to source 76 | sourceSets.main.java.srcDirs += 'src/main/generated-sources' 77 | 78 | // Run jaxb task before compile Java classes 79 | compileJava.dependsOn jaxb -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/XmlApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.xml; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class XmlApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(XmlApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/adapter/DateAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.adapter; 2 | 3 | import javax.xml.bind.annotation.adapters.XmlAdapter; 4 | import java.time.LocalDate; 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.format.DateTimeParseException; 7 | import java.util.Objects; 8 | 9 | public class DateAdapter extends XmlAdapter { 10 | 11 | @Override 12 | public LocalDate unmarshal(String v) { 13 | if (Objects.nonNull(v)) { 14 | try { 15 | return LocalDate.parse(v); 16 | } catch (DateTimeParseException e) { 17 | throw new RuntimeException("Failed to parse date: " + v, e); 18 | } 19 | } 20 | return null; 21 | } 22 | 23 | @Override 24 | public String marshal(LocalDate v) { 25 | if (Objects.nonNull(v)) { 26 | return v.format(DateTimeFormatter.ISO_DATE); 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/adapter/DateTimeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.adapter; 2 | 3 | import javax.xml.bind.annotation.adapters.XmlAdapter; 4 | import java.time.LocalDateTime; 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.format.DateTimeParseException; 7 | import java.util.Objects; 8 | 9 | public class DateTimeAdapter extends XmlAdapter { 10 | 11 | @Override 12 | public LocalDateTime unmarshal(String v) { 13 | if (Objects.nonNull(v)) { 14 | try { 15 | return LocalDateTime.parse(v); 16 | } catch (DateTimeParseException e) { 17 | throw new RuntimeException("Failed to parse datetime: " + v, e); 18 | } 19 | } 20 | return null; 21 | } 22 | 23 | @Override 24 | public String marshal(LocalDateTime v) { 25 | if (Objects.nonNull(v)) { 26 | return v.format(DateTimeFormatter.ISO_DATE_TIME); 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/adapter/EnumValueDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.adapter; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import org.jvnet.jaxb2_commons.lang.EnumValue; 7 | 8 | import java.io.IOException; 9 | 10 | public class EnumValueDeserializer extends JsonSerializer { 11 | 12 | @Override 13 | public void serialize(EnumValue value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 14 | gen.writeString(value.enumValue().toString()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/adapter/TimeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.adapter; 2 | 3 | import javax.xml.bind.annotation.adapters.XmlAdapter; 4 | import java.time.LocalTime; 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.format.DateTimeParseException; 7 | import java.util.Objects; 8 | 9 | public class TimeAdapter extends XmlAdapter { 10 | 11 | @Override 12 | public LocalTime unmarshal(String v) { 13 | if (Objects.nonNull(v)) { 14 | try { 15 | return LocalTime.parse(v); 16 | } catch (DateTimeParseException e) { 17 | throw new RuntimeException("Failed to parse time: " + v, e); 18 | } 19 | } 20 | return null; 21 | } 22 | 23 | @Override 24 | public String marshal(LocalTime v) { 25 | if (Objects.nonNull(v)) { 26 | return v.format(DateTimeFormatter.ISO_TIME); 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/controller/XmlController.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.controller; 2 | 3 | import com.example.xml.service.XmlParserService; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | @RestController 10 | @RequestMapping("/xml") 11 | @RequiredArgsConstructor 12 | public class XmlController { 13 | 14 | private final XmlParserService xmlParserService; 15 | 16 | @PostMapping("/{entityType}/json") 17 | @ResponseStatus(HttpStatus.CREATED) 18 | public Object parseXmlToJson(@PathVariable String entityType, @RequestBody String xml) throws ClassNotFoundException, JsonProcessingException { 19 | return xmlParserService.toJson(Class.forName("com.example.jaxb." + entityType), xml); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /springboot-xml/src/main/java/com/example/xml/service/XmlParserService.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.service; 2 | 3 | import com.example.xml.adapter.EnumValueDeserializer; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import com.fasterxml.jackson.databind.SerializationFeature; 9 | import com.fasterxml.jackson.databind.module.SimpleModule; 10 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 11 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 12 | import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; 13 | import org.jvnet.jaxb2_commons.lang.EnumValue; 14 | import org.springframework.stereotype.Service; 15 | 16 | @Service 17 | public class XmlParserService { 18 | 19 | private static final XmlMapper xmlMapper = XmlMapper.builder() 20 | .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) 21 | .addModule(new JaxbAnnotationModule()) 22 | .build(); 23 | 24 | private static final ObjectMapper objectMapper = new ObjectMapper(); 25 | 26 | static { 27 | SimpleModule module = new SimpleModule(); 28 | module.addSerializer(EnumValue.class, new EnumValueDeserializer()); 29 | objectMapper.registerModule(module); 30 | objectMapper.registerModule(new JavaTimeModule()); 31 | objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 32 | } 33 | 34 | public JsonNode toJson(Class classType, String xml) throws JsonProcessingException { 35 | Object obj = xmlMapper.reader().forType(classType).readValue(xml); 36 | return objectMapper.convertValue(obj, JsonNode.class); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /springboot-xml/src/main/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashishlahoti/springboot-examples/e21cb39475a3d7b389546e743e833156c2939f4c/springboot-xml/src/main/resources/application.yml -------------------------------------------------------------------------------- /springboot-xml/src/main/resources/jaxb/bindings.xjb: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 13 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /springboot-xml/src/main/resources/schema/schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ISO-4217 3-letter currency codes, 41 | as defined at 42 | http://www.bsi-global.com/Technical+Information/Publications/_Publications/tig90.xalter 43 | or available from 44 | http://www.xe.com/iso4217.htm 45 | Only a subset are defined here. 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Whether the interest is 65 | rounded up, down or to the 66 | nearest round value. 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /springboot-xml/src/test/java/com/example/xml/XmlApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.xml; 2 | 3 | import com.example.xml.controller.XmlController; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @SpringBootTest 11 | class XmlApplicationTests { 12 | 13 | @Autowired 14 | private XmlController xmlController; 15 | 16 | @Test 17 | void contextLoads() { 18 | assertThat(xmlController).isNotNull(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /springboot-xml/src/test/java/com/example/xml/controller/XmlControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.controller; 2 | 3 | import com.example.xml.service.XmlParserService; 4 | import org.apache.commons.io.IOUtils; 5 | import org.junit.jupiter.api.Test; 6 | import org.mockito.InjectMocks; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.core.io.ClassPathResource; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.result.MockMvcResultHandlers; 13 | 14 | import java.io.IOException; 15 | import java.nio.charset.StandardCharsets; 16 | 17 | import static org.hamcrest.Matchers.equalTo; 18 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 21 | 22 | @SpringBootTest 23 | @AutoConfigureMockMvc 24 | public class XmlControllerTest { 25 | 26 | @Autowired 27 | private MockMvc mockMvc; 28 | 29 | @InjectMocks 30 | private XmlParserService xmlParserService; 31 | 32 | @Test 33 | public void parseXmlToJson_whenValidRequest_returnsValidResponse() throws Exception { 34 | String xml = read("data/accountSummary.xml"); 35 | mockMvc.perform(post("/xml/AccountSummary/json").content(xml)) 36 | .andExpect(status().is2xxSuccessful()) 37 | .andDo(MockMvcResultHandlers.print()) 38 | .andExpect(jsonPath("$.currency", equalTo("USD"))) 39 | .andExpect(jsonPath("$.balance", equalTo(2703.35))) 40 | .andExpect(jsonPath("$.interest.value", equalTo(27.55))) 41 | .andExpect(jsonPath("$.interest.rounding", equalTo("half up"))) 42 | .andExpect(jsonPath("$.version", equalTo("1.0"))); 43 | } 44 | 45 | private String read(String location) throws IOException { 46 | return IOUtils.toString(new ClassPathResource(location).getInputStream(), StandardCharsets.UTF_8); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /springboot-xml/src/test/java/com/example/xml/service/XmlParserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.xml.service; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import org.apache.commons.io.IOUtils; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.core.io.ClassPathResource; 9 | 10 | import java.io.IOException; 11 | import java.nio.charset.StandardCharsets; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @SpringBootTest 16 | public class XmlParserServiceTest { 17 | 18 | @Autowired 19 | private XmlParserService xmlParserService; 20 | 21 | @Test 22 | public void toJson_whenValidClassAndXML_returnJson() throws ClassNotFoundException, IOException { 23 | String xml = read("data/accountSummary.xml"); 24 | JsonNode json = xmlParserService.toJson(Class.forName("com.example.jaxb.AccountSummary"), xml); 25 | assertThat(json.asText()).isNotNull(); 26 | } 27 | 28 | private String read(String location) throws IOException { 29 | return IOUtils.toString(new ClassPathResource(location).getInputStream(), StandardCharsets.UTF_8); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /springboot-xml/src/test/resources/data/accountSummary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2003-01-01T12:25:00 4 | USD 5 | 2703.35 6 | 27.55 7 | --------------------------------------------------------------------------------