BAEL-3091: The Prototype Pattern in Java (changed code based on valid comments from a reader)

This commit is contained in:
Vivek Balasubramaniam
2019-10-29 22:27:15 +05:30
parent db85c8f275
commit d3d5b060e7
20517 changed files with 1642290 additions and 0 deletions
@@ -0,0 +1,64 @@
package org.baeldung.errorhandling;
import java.util.Arrays;
import java.util.List;
import org.springframework.http.HttpStatus;
public class ApiError {
private HttpStatus status;
private String message;
private List<String> errors;
//
public ApiError() {
super();
}
public ApiError(final HttpStatus status, final String message, final List<String> errors) {
super();
this.status = status;
this.message = message;
this.errors = errors;
}
public ApiError(final HttpStatus status, final String message, final String error) {
super();
this.status = status;
this.message = message;
errors = Arrays.asList(error);
}
//
public HttpStatus getStatus() {
return status;
}
public void setStatus(final HttpStatus status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(final String message) {
this.message = message;
}
public List<String> getErrors() {
return errors;
}
public void setErrors(final List<String> errors) {
this.errors = errors;
}
public void setError(final String error) {
errors = Arrays.asList(error);
}
}
@@ -0,0 +1,169 @@
package org.baeldung.errorhandling;
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
// 400
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final List<String> errors = new ArrayList<String>();
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.add(error.getField() + ": " + error.getDefaultMessage());
}
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
}
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
}
@Override
protected ResponseEntity<Object> handleBindException(final BindException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final List<String> errors = new ArrayList<String>();
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.add(error.getField() + ": " + error.getDefaultMessage());
}
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
}
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
}
@Override
protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType();
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
@Override
protected ResponseEntity<Object> handleMissingServletRequestPart(final MissingServletRequestPartException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final String error = ex.getRequestPartName() + " part is missing";
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
@Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(final MissingServletRequestParameterException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final String error = ex.getParameterName() + " parameter is missing";
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
//
@ExceptionHandler({ MethodArgumentTypeMismatchException.class })
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName();
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
@ExceptionHandler({ ConstraintViolationException.class })
public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException ex, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final List<String> errors = new ArrayList<String>();
for (final ConstraintViolation<?> violation : ex.getConstraintViolations()) {
errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage());
}
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
// 404
@Override
protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL();
final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), error);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
// 405
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final StringBuilder builder = new StringBuilder();
builder.append(ex.getMethod());
builder.append(" method is not supported for this request. Supported methods are ");
ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " "));
final ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, ex.getLocalizedMessage(), builder.toString());
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
// 415
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final StringBuilder builder = new StringBuilder();
builder.append(ex.getContentType());
builder.append(" media type is not supported. Supported media types are ");
ex.getSupportedMediaTypes().forEach(t -> builder.append(t + " "));
final ApiError apiError = new ApiError(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getLocalizedMessage(), builder.substring(0, builder.length() - 2));
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
// 500
@ExceptionHandler({ Exception.class })
public ResponseEntity<Object> handleAll(final Exception ex, final WebRequest request) {
logger.info(ex.getClass().getName());
logger.error("error", ex);
//
final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
}
@@ -0,0 +1,78 @@
package org.baeldung.persistence.model;
import java.io.Serializable;
import javax.validation.constraints.Size;
public class Foo implements Serializable {
private static final long serialVersionUID = -5422285893276747592L;
private long id;
@Size(min = 5, max = 14)
private String name;
public Foo() {
super();
}
public Foo(final String name) {
super();
this.name = name;
}
// API
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
//
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Foo other = (Foo) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Foo [name=").append(name).append("]");
return builder.toString();
}
}
@@ -0,0 +1,80 @@
package org.baeldung.security;
import org.baeldung.security.web.MySavedRequestAwareAuthenticationSuccessHandler;
import org.baeldung.security.web.RestAuthenticationEntryPoint;
import org.baeldung.web.error.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan("org.baeldung.security")
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private MySavedRequestAwareAuthenticationSuccessHandler mySuccessHandler;
private SimpleUrlAuthenticationFailureHandler myFailureHandler = new SimpleUrlAuthenticationFailureHandler();
public SecurityJavaConfig() {
super();
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
.and()
.withUser("user").password(encoder().encode("userPass")).roles("USER");
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/csrfAttacker*").permitAll()
.antMatchers("/api/customer/**").permitAll()
.antMatchers("/api/foos/**").authenticated()
.antMatchers("/api/async/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.successHandler(mySuccessHandler)
.failureHandler(myFailureHandler)
.and()
.httpBasic()
.and()
.logout();
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
@@ -0,0 +1,43 @@
package org.baeldung.security.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class MySavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws ServletException, IOException {
final SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
clearAuthenticationAttributes(request);
return;
}
final String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
clearAuthenticationAttributes(request);
return;
}
clearAuthenticationAttributes(request);
}
public void setRequestCache(final RequestCache requestCache) {
this.requestCache = requestCache;
}
}
@@ -0,0 +1,27 @@
package org.baeldung.security.web;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
/**
* The Entry Point will not redirect to any sort of Login - it will return the 401
*/
@Component
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(
final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
@@ -0,0 +1,10 @@
package org.baeldung.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebMvc
@Configuration
public class ClientWebConfig implements WebMvcConfigurer {
}
@@ -0,0 +1,14 @@
package org.baeldung.spring;
import org.springframework.context.annotation.ComponentScan;
// @Configuration
// @ImportResource({ "classpath:webSecurityConfig.xml" })
@ComponentScan("org.baeldung.security")
public class SecurityXmlConfig {
public SecurityXmlConfig() {
super();
}
}
@@ -0,0 +1,40 @@
package org.baeldung.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ComponentScan("org.baeldung.web")
@EnableWebMvc
@EnableAsync
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Bean
public ViewResolver viewResolver() {
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/csrfAttacker.html");
}
}
@@ -0,0 +1,46 @@
package org.baeldung.swagger2;
import static com.google.common.collect.Lists.newArrayList;
import java.util.Collections;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("org.baeldung.web.controller"))
.paths(PathSelectors.ant("/foos/*"))
.build()
.apiInfo(apiInfo())
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, newArrayList(new ResponseMessageBuilder().code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build(),
new ResponseMessageBuilder().code(403)
.message("Forbidden!!!!!")
.build()));
}
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", new Contact("John Doe", "www.example.com", "myeaddress@company.com"), "License of API", "API license URL", Collections.emptyList());
return apiInfo;
}
}
@@ -0,0 +1,39 @@
package org.baeldung.web.controller;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.baeldung.web.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AsyncController {
private static final Logger log = LoggerFactory.getLogger(AsyncService.class);
@Autowired
private AsyncService asyncService;
@RequestMapping(method = RequestMethod.GET, value = "/async")
@ResponseBody
public Object standardProcessing() throws Exception {
log.info("Outside the @Async logic - before the async call: {}", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
asyncService.asyncCall();
log.info("Inside the @Async logic - after the async call: {}", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
@RequestMapping(method = RequestMethod.GET, value = "/async2")
@ResponseBody
public Callable<Boolean> springMVCAsyncTest() {
return asyncService.checkIfPrincipalPropagated();
}
}
@@ -0,0 +1,14 @@
package org.baeldung.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class CustomController {
@RequestMapping(value = "/custom", method = RequestMethod.POST)
public String custom() {
return "custom";
}
}
@@ -0,0 +1,49 @@
package org.baeldung.web.controller;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.baeldung.persistence.model.Foo;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.util.UriComponentsBuilder;
import com.google.common.collect.Lists;
@Controller
@RequestMapping(value = "/foos")
public class FooController {
// API
// read - single
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public Foo findById(@PathVariable("id") final Long id, final UriComponentsBuilder uriBuilder, final HttpServletResponse response) {
return new Foo(randomAlphabetic(6));
}
// read - multiple
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<Foo> findAll() {
return Lists.newArrayList(new Foo(randomAlphabetic(6)));
}
// write - just for test
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
@ResponseBody
public Foo create(@RequestBody final Foo foo) {
return foo;
}
}
@@ -0,0 +1,30 @@
package org.baeldung.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class RootController {
public RootController() {
super();
}
// API
@RequestMapping(value = "/admin/x", method = RequestMethod.GET)
@ResponseBody
public String sampleAdminPage() {
return "Hello";
}
@RequestMapping(value = "/my-error-page", method = RequestMethod.GET)
@ResponseBody
public String sampleErrorPage() {
return "Error Occurred";
}
}
@@ -0,0 +1,23 @@
package org.baeldung.web.error;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(final HttpServletRequest request, final HttpServletResponse response, final AccessDeniedException ex) throws IOException, ServletException {
response.getOutputStream().print("Error Message Goes Here");
response.setStatus(403);
// response.sendRedirect("/my-error-page");
}
}
@@ -0,0 +1,74 @@
package org.baeldung.web.error;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
//import org.springframework.security.access.AccessDeniedException;
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
// API
// 400
@ExceptionHandler({ DataIntegrityViolationException.class })
public ResponseEntity<Object> handleBadRequest(final DataIntegrityViolationException ex, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(final HttpMessageNotReadableException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
// ex.getCause() instanceof JsonMappingException, JsonParseException // for additional information later on
return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request);
}
// 403
@ExceptionHandler({ AccessDeniedException.class })
public ResponseEntity<Object> handleAccessDeniedException(final Exception ex, final WebRequest request) {
System.out.println("request" + request.getUserPrincipal());
return new ResponseEntity<Object>("Access denied message here", new HttpHeaders(), HttpStatus.FORBIDDEN);
}
// 409
@ExceptionHandler({ InvalidDataAccessApiUsageException.class, DataAccessException.class })
protected ResponseEntity<Object> handleConflict(final RuntimeException ex, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
}
// 412
// 500
@ExceptionHandler({ NullPointerException.class, IllegalArgumentException.class, IllegalStateException.class })
/*500*/public ResponseEntity<Object> handleInternal(final RuntimeException ex, final WebRequest request) {
logger.error("500 Status Code", ex);
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
}
@@ -0,0 +1,21 @@
package org.baeldung.web.exception;
public final class MyResourceNotFoundException extends RuntimeException {
public MyResourceNotFoundException() {
super();
}
public MyResourceNotFoundException(final String message, final Throwable cause) {
super(message, cause);
}
public MyResourceNotFoundException(final String message) {
super(message);
}
public MyResourceNotFoundException(final Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,11 @@
package org.baeldung.web.service;
import java.util.concurrent.Callable;
public interface AsyncService {
void asyncCall();
Callable<Boolean> checkIfPrincipalPropagated();
}
@@ -0,0 +1,36 @@
package org.baeldung.web.service;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
@Service
public class AsyncServiceImpl implements AsyncService {
private static final Logger log = LoggerFactory.getLogger(AsyncService.class);
@Async
@Override
public void asyncCall() {
log.info("Inside the @Async logic: {}", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}
@Override
public Callable<Boolean> checkIfPrincipalPropagated() {
Object before = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("Before new thread: {}", before);
return new Callable<Boolean>() {
public Boolean call() throws Exception {
Object after = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("New thread: {}", after);
return before == after;
}
};
}
}
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="WARN" />
<logger name="org.springframework.transaction" level="WARN" />
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
<logger name="org.springframework.web.servlet.mvc" level="WARN" />
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Spring Security Configurations are managed in SecurityJavaConfig class -->
<!--
<http use-expressions="true" entry-point-ref="restAuthenticationEntryPoint">
<intercept-url pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/>
<intercept-url pattern="/api/**" access="isAuthenticated()" />
<csrf disabled="true" />
<form-login authentication-success-handler-ref="mySuccessHandler"
authentication-failure-handler-ref="myFailureHandler" />
<access-denied-handler error-page="/my-error-page" />
<access-denied-handler ref="customAccessDeniedHandler" />
<logout />
</http>
<beans:bean id="mySuccessHandler"
class="org.baeldung.security.MySavedRequestAwareAuthenticationSuccessHandler" />
<beans:bean id="myFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler" />
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="temporary" password="temporary" authorities="ROLE_ADMIN" />
<user name="user" password="userPass" authorities="ROLE_USER" />
<user name="user1" password="user1Pass" authorities="ROLE_USER"/>
<user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>
<global-method-security pre-post-annotations="enabled"/>
-->
</beans:beans>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1000000" />
</bean>
</beans>
@@ -0,0 +1,16 @@
<html>
<head></head>
<body>
<h1>CSRF Attacker</h1>
<a href="http://localhost:8080/spring-security-rest-full/transfer?accountNo=5678&amount=1000">Show Kittens Pictures</a>
<img src="http://localhost:8080/spring-security-rest-full/transfer?accountNo=5678&amount=1000"/>
<form action="http://localhost:8080/spring-security-rest-full/transfer" method="POST">
<input name="accountNo" type="hidden" value="5678"/>
<input name="amount" type="hidden" value="1000"/>
<input type="submit" value="Show Kittens Picture">
</form>
</body>
</html>
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>Spring MVC Application</display-name>
<!-- Spring root -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>org.baeldung.spring</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring child -->
<servlet>
<servlet-name>api</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>api</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<!-- <welcome-file-list> -->
<!-- <welcome-file>index.html</welcome-file> -->
<!-- </welcome-file-list> -->
</web-app>
@@ -0,0 +1,20 @@
package org.baeldung;
import org.baeldung.security.SecurityJavaConfig;
import org.baeldung.spring.ClientWebConfig;
import org.baeldung.spring.WebConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { ClientWebConfig.class, SecurityJavaConfig.class, WebConfig.class })
public class SpringContextIntegrationTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -0,0 +1,20 @@
package org.baeldung;
import org.baeldung.security.SecurityJavaConfig;
import org.baeldung.spring.ClientWebConfig;
import org.baeldung.spring.WebConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { ClientWebConfig.class, SecurityJavaConfig.class, WebConfig.class })
public class SpringContextTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -0,0 +1,88 @@
package org.baeldung.errorhandling;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.baeldung.errorhandling.ApiError;
import org.baeldung.web.TestConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
import com.jayway.restassured.specification.RequestSpecification;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class)
public class FooLiveTest {
private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest";
// private FormAuthConfig formConfig = new FormAuthConfig(URL_PREFIX + "/login", "temporary", "temporary");
private String cookie;
private RequestSpecification givenAuth() {
// return RestAssured.given().auth().form("user", "userPass", formConfig);
// if (cookie == null) {
// cookie = RestAssured.given().contentType("application/x-www-form-urlencoded").formParam("password", "userPass").formParam("username", "user").post(URL_PREFIX + "/login").getCookie("JSESSIONID");
// }
// return RestAssured.given().cookie("JSESSIONID", cookie);
return RestAssured.given()
.auth().preemptive()
.basic("user", "userPass");
}
@Test
public void whenTry_thenOK() {
final Response response = givenAuth().get(URL_PREFIX + "/api/foos");
assertEquals(200, response.statusCode());
System.out.println(response.asString());
}
@Test
public void whenMethodArgumentMismatch_thenBadRequest() {
final Response response = givenAuth().get(URL_PREFIX + "/api/foos/ccc");
final ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.BAD_REQUEST, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("should be of type"));
}
@Test
public void whenNoHandlerForHttpRequest_thenNotFound() {
final Response response = givenAuth().delete(URL_PREFIX + "/api/xx");
final ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.NOT_FOUND, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("No handler found"));
System.out.println(response.asString());
}
@Test
public void whenHttpRequestMethodNotSupported_thenMethodNotAllowed() {
final Response response = givenAuth().delete(URL_PREFIX + "/api/foos/1");
final ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.METHOD_NOT_ALLOWED, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("Supported methods are"));
System.out.println(response.asString());
}
@Test
public void whenSendInvalidHttpMediaType_thenUnsupportedMediaType() {
final Response response = givenAuth().body("").post(URL_PREFIX + "/api/foos");
final ApiError error = response.as(ApiError.class);
assertEquals(HttpStatus.UNSUPPORTED_MEDIA_TYPE, error.getStatus());
assertEquals(1, error.getErrors().size());
assertTrue(error.getErrors().get(0).contains("media type is not supported"));
System.out.println(response.asString());
}
}
@@ -0,0 +1,48 @@
package org.baeldung.web;
import org.baeldung.security.SecurityJavaConfig;
import org.baeldung.spring.ClientWebConfig;
import org.baeldung.spring.WebConfig;
import org.baeldung.web.controller.AsyncController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { ClientWebConfig.class, SecurityJavaConfig.class, WebConfig.class })
public class AsyncControllerIntegrationTest {
@Autowired
WebApplicationContext wac;
@Autowired
MockHttpSession session;
@Mock
AsyncController controller;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void testAsync() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/async")).andExpect(status().is5xxServerError());
}
}
@@ -0,0 +1,20 @@
package org.baeldung.web;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
public class SwaggerLiveTest {
private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest/api";
@Test
public void whenVerifySpringFoxIsWorking_thenOK() {
final Response response = RestAssured.get(URL_PREFIX + "/v2/api-docs");
assertEquals(200, response.statusCode());
System.out.println(response.asString());
}
}
@@ -0,0 +1,19 @@
package org.baeldung.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@Configuration
@ComponentScan({ "org.baeldung.web" })
public class TestConfig {
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}
@@ -0,0 +1,13 @@
*.class
#folders#
/target
/neoDb*
/data
/src/main/webapp/WEB-INF/classes
*/META-INF/*
# Packaged files #
*.jar
*.war
*.ear