diff --git a/spring-boot-libraries/pom.xml b/spring-boot-libraries/pom.xml
index c28128c5f0..66aa66bdfd 100644
--- a/spring-boot-libraries/pom.xml
+++ b/spring-boot-libraries/pom.xml
@@ -1,144 +1,156 @@
- 4.0.0
- spring-boot-libraries
- war
- spring-boot-libraries
- This is simple boot application for Spring boot actuator test
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ spring-boot-libraries
+ war
+ spring-boot-libraries
+ This is simple boot application for Spring boot actuator test
-
- parent-boot-2
- com.baeldung
- 0.0.1-SNAPSHOT
- ../parent-boot-2
-
+
+ parent-boot-2
+ com.baeldung
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-tomcat
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.zalando
+ problem-spring-web
+ ${problem-spring-web.version}
+
-
-
- net.javacrumbs.shedlock
- shedlock-spring
- 2.1.0
-
-
- net.javacrumbs.shedlock
- shedlock-provider-jdbc-template
- 2.1.0
-
+
+
+ net.javacrumbs.shedlock
+ shedlock-spring
+ 2.1.0
+
+
+ net.javacrumbs.shedlock
+ shedlock-provider-jdbc-template
+ 2.1.0
+
-
+
-
- spring-boot
-
-
- src/main/resources
- true
-
-
+
+ spring-boot
+
+
+ src/main/resources
+ true
+
+
-
+
-
- org.apache.maven.plugins
- maven-war-plugin
-
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
-
- pl.project13.maven
- git-commit-id-plugin
- ${git-commit-id-plugin.version}
-
-
- get-the-git-infos
-
- revision
-
- initialize
-
-
- validate-the-git-infos
-
- validateRevision
-
- package
-
-
-
- true
- ${project.build.outputDirectory}/git.properties
-
-
+
+ pl.project13.maven
+ git-commit-id-plugin
+ ${git-commit-id-plugin.version}
+
+
+ get-the-git-infos
+
+ revision
+
+ initialize
+
+
+ validate-the-git-infos
+
+ validateRevision
+
+ package
+
+
+
+ true
+ ${project.build.outputDirectory}/git.properties
+
+
-
+
-
+
-
-
- autoconfiguration
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- integration-test
-
- test
-
-
-
- **/*LiveTest.java
- **/*IntegrationTest.java
- **/*IntTest.java
-
-
- **/AutoconfigurationTest.java
-
-
-
-
-
-
- json
-
-
-
-
-
-
-
+
+
+ autoconfiguration
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration-test
+
+ test
+
+
+
+ **/*LiveTest.java
+ **/*IntegrationTest.java
+ **/*IntTest.java
+
+
+ **/AutoconfigurationTest.java
+
+
+
+
+
+
+ json
+
+
+
+
+
+
+
-
-
- com.baeldung.intro.App
- 8.5.11
- 2.4.1.Final
- 1.9.0
- 2.0.0
- 5.0.2
- 5.0.2
- 5.2.4
- 18.0
- 2.2.4
- 2.3.2
-
+
+
+ com.baeldung.intro.App
+ 8.5.11
+ 2.4.1.Final
+ 1.9.0
+ 2.0.0
+ 5.0.2
+ 5.0.2
+ 5.2.4
+ 18.0
+ 2.2.4
+ 2.3.2
+ 0.23.0
+
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/Application.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/Application.java
similarity index 93%
rename from spring-boot-libraries/src/main/java/com/baeldung/Application.java
rename to spring-boot-libraries/src/main/java/com/baeldung/boot/Application.java
index c1b6558b26..cb0d0c1532 100644
--- a/spring-boot-libraries/src/main/java/com/baeldung/Application.java
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/Application.java
@@ -1,4 +1,4 @@
-package org.baeldung.boot;
+package com.baeldung.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/SpringProblemApplication.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/SpringProblemApplication.java
new file mode 100644
index 0000000000..7ca9881fb9
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/SpringProblemApplication.java
@@ -0,0 +1,19 @@
+package com.baeldung.boot.problem;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)
+@ComponentScan("com.baeldung.boot.problem")
+public class SpringProblemApplication {
+
+ public static void main(String[] args) {
+ System.setProperty("spring.profiles.active", "problem");
+ SpringApplication.run(SpringProblemApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/advice/ExceptionHandler.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/advice/ExceptionHandler.java
new file mode 100644
index 0000000000..7b4cbac7f7
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/advice/ExceptionHandler.java
@@ -0,0 +1,9 @@
+package com.baeldung.boot.problem.advice;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.zalando.problem.spring.web.advice.ProblemHandling;
+
+@ControllerAdvice
+public class ExceptionHandler implements ProblemHandling {
+
+}
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/advice/SecurityExceptionHandler.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/advice/SecurityExceptionHandler.java
new file mode 100644
index 0000000000..8013cbf5c3
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/advice/SecurityExceptionHandler.java
@@ -0,0 +1,9 @@
+package com.baeldung.boot.problem.advice;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;
+
+@ControllerAdvice
+public class SecurityExceptionHandler implements SecurityAdviceTrait {
+
+}
\ No newline at end of file
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/configuration/ProblemDemoConfiguration.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/configuration/ProblemDemoConfiguration.java
new file mode 100644
index 0000000000..209ff553c7
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/configuration/ProblemDemoConfiguration.java
@@ -0,0 +1,17 @@
+package com.baeldung.boot.problem.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.zalando.problem.ProblemModule;
+import org.zalando.problem.validation.ConstraintViolationProblemModule;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Configuration
+public class ProblemDemoConfiguration {
+
+ @Bean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper().registerModules(new ProblemModule(), new ConstraintViolationProblemModule());
+ }
+}
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/configuration/SecurityConfiguration.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/configuration/SecurityConfiguration.java
new file mode 100644
index 0000000000..0cb8048981
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/configuration/SecurityConfiguration.java
@@ -0,0 +1,31 @@
+package com.baeldung.boot.problem.configuration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
+
+@Configuration
+@EnableWebSecurity
+@Import(SecurityProblemSupport.class)
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ private SecurityProblemSupport problemSupport;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf().disable();
+
+ http.authorizeRequests()
+ .antMatchers("/")
+ .permitAll();
+
+ http.exceptionHandling()
+ .authenticationEntryPoint(problemSupport)
+ .accessDeniedHandler(problemSupport);
+ }
+}
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/controller/ProblemDemoController.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/controller/ProblemDemoController.java
new file mode 100644
index 0000000000..50f1ad5137
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/controller/ProblemDemoController.java
@@ -0,0 +1,56 @@
+package com.baeldung.boot.problem.controller;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.http.MediaType;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.boot.problem.dto.Task;
+import com.baeldung.boot.problem.problems.TaskNotFoundProblem;
+
+@RestController
+@RequestMapping("/tasks")
+public class ProblemDemoController {
+
+ private static final Map MY_TASKS;
+
+ static {
+ MY_TASKS = new HashMap<>();
+ MY_TASKS.put(1L, new Task(1L, "My first task"));
+ MY_TASKS.put(2L, new Task(2L, "My second task"));
+ }
+
+ @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+ public List getTasks() {
+ return new ArrayList<>(MY_TASKS.values());
+ }
+
+ @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public Task getTasks(@PathVariable("id") Long taskId) {
+ if (MY_TASKS.containsKey(taskId)) {
+ return MY_TASKS.get(taskId);
+ } else {
+ throw new TaskNotFoundProblem(taskId);
+ }
+ }
+
+ @PutMapping("/{id}")
+ public void updateTask(@PathVariable("id") Long id) {
+ throw new UnsupportedOperationException();
+ }
+
+ @DeleteMapping("/{id}")
+ public void deleteTask(@PathVariable("id") Long id) {
+ throw new AccessDeniedException("You can't delete this task");
+ }
+
+}
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/dto/Task.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/dto/Task.java
new file mode 100644
index 0000000000..a5f39474e7
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/dto/Task.java
@@ -0,0 +1,32 @@
+package com.baeldung.boot.problem.dto;
+
+public class Task {
+
+ private Long id;
+ private String description;
+
+ public Task() {
+ }
+
+ public Task(Long id, String description) {
+ this.id = id;
+ this.description = description;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+}
diff --git a/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/problems/TaskNotFoundProblem.java b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/problems/TaskNotFoundProblem.java
new file mode 100644
index 0000000000..cc3f21d4a5
--- /dev/null
+++ b/spring-boot-libraries/src/main/java/com/baeldung/boot/problem/problems/TaskNotFoundProblem.java
@@ -0,0 +1,16 @@
+package com.baeldung.boot.problem.problems;
+
+import java.net.URI;
+
+import org.zalando.problem.AbstractThrowableProblem;
+import org.zalando.problem.Status;
+
+public class TaskNotFoundProblem extends AbstractThrowableProblem {
+
+ private static final URI TYPE = URI.create("https://example.org/not-found");
+
+ public TaskNotFoundProblem(Long taskId) {
+ super(TYPE, "Not found", Status.NOT_FOUND, String.format("Task '%s' not found", taskId));
+ }
+
+}
diff --git a/spring-boot-libraries/src/main/resources/application-problem.properties b/spring-boot-libraries/src/main/resources/application-problem.properties
new file mode 100644
index 0000000000..7d0b0a2720
--- /dev/null
+++ b/spring-boot-libraries/src/main/resources/application-problem.properties
@@ -0,0 +1,3 @@
+spring.resources.add-mappings=false
+spring.mvc.throw-exception-if-no-handler-found=true
+spring.http.encoding.force=true
diff --git a/spring-boot-libraries/src/test/java/com/baeldung/boot/problem/controller/ProblemDemoControllerIntegrationTest.java b/spring-boot-libraries/src/test/java/com/baeldung/boot/problem/controller/ProblemDemoControllerIntegrationTest.java
new file mode 100644
index 0000000000..3b7e43a565
--- /dev/null
+++ b/spring-boot-libraries/src/test/java/com/baeldung/boot/problem/controller/ProblemDemoControllerIntegrationTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.boot.problem.controller;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import com.baeldung.boot.problem.SpringProblemApplication;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = SpringProblemApplication.class)
+@AutoConfigureMockMvc
+public class ProblemDemoControllerIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void whenRequestingAllTasks_thenReturnSuccessfulResponseWithArrayWithTwoTasks() throws Exception {
+ mockMvc.perform(get("/tasks").contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andDo(print())
+ .andExpect(jsonPath("$.length()", equalTo(2)))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void whenRequestingExistingTask_thenReturnSuccessfulResponse() throws Exception {
+ mockMvc.perform(get("/tasks/1").contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andDo(print())
+ .andExpect(jsonPath("$.id", equalTo(1)))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void whenRequestingMissingTask_thenReturnNotFoundProblemResponse() throws Exception {
+ mockMvc.perform(get("/tasks/5").contentType(MediaType.APPLICATION_PROBLEM_JSON_VALUE))
+ .andDo(print())
+ .andExpect(jsonPath("$.title", equalTo("Not found")))
+ .andExpect(jsonPath("$.status", equalTo(404)))
+ .andExpect(jsonPath("$.detail", equalTo("Task '5' not found")))
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ public void whenMakePutCall_thenReturnNotImplementedProblemResponse() throws Exception {
+ mockMvc.perform(put("/tasks/1").contentType(MediaType.APPLICATION_PROBLEM_JSON_VALUE))
+ .andDo(print())
+ .andExpect(jsonPath("$.title", equalTo("Not Implemented")))
+ .andExpect(jsonPath("$.status", equalTo(501)))
+ .andExpect(status().isNotImplemented());
+ }
+
+ @Test
+ public void whenMakeDeleteCall_thenReturnForbiddenProblemResponse() throws Exception {
+ mockMvc.perform(delete("/tasks/2").contentType(MediaType.APPLICATION_PROBLEM_JSON_VALUE))
+ .andDo(print())
+ .andExpect(jsonPath("$.title", equalTo("Forbidden")))
+ .andExpect(jsonPath("$.status", equalTo(403)))
+ .andExpect(jsonPath("$.detail", equalTo("You can't delete this task")))
+ .andExpect(status().isForbidden());
+ }
+
+}