Wednesday, January 16, 2019

Create Global Exception with Custom error message for Springboot REST Application

I have seen many projects in SpringBoot has written with multiple Exception classes. This is a huge mistake, hence thus this leads to

  • accumulation of Exception classes 
  • and hence methods are being getting longer with so many exceptions at the end.
Another issue is some people has written error code inside the error message. This is mostly due to not understanding how to make custom error object.

Below will sample code explaining adding a global exception and a custom error object with error message, error code  etc.

What we create in this example
1. Custom error object         = ApiError.java
2. Custom exception class    = CustomException
3. Handler class to handle the global exception = GlobalExceptionHandler

Important:
I'm using "Lombok" , so all getter, setter, builder code is being generated by Lombok.

***************************************************************************

import java.util.Date;
import java.util.List;

import org.springframework.http.HttpStatus;

import lombok.Builder;
import lombok.Data;

@Data
@Builder

public class ApiError{


private HttpStatus status;
private String code;
private String message;
private List errors;
private String path;
private Date timeStamp;
private String source;
}


-------------------------------------------------------------------------------------

public class CustomException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1581305064814093777L;

/**
* error code for the {@link CustomException}
*/
private String code;

public CustomException() {
super();
}

public CustomException(final String message) {
super(message);
}

public CustomException(final String errorCode, final String message) {
super(message);
this.code = errorCode;
}

public CustomException(final String message, final Throwable cause) {
super(message, cause);
}

public CustomException(final String code, final String message, final Throwable cause) {
super(message, cause);
this.code = code;
}

public CustomException(final Throwable cause) {
super(cause);
}

/**
* Custom error code for the REST project.Code consist of 10 digits. Below is
* the breakdown of the code description.
*
* @return error code for specific error.
*/
public String getCode() {
return code;
}

}


--------------------------------------------------------------------------------------------------------

import java.util.Arrays;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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;



/**
 * Common exception handler for REST.
 *
 * @author AMAL PRASAD
 * @version 1.0.0
 * @since 1.0.0
 */
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

private static final String REQUEST_PATH = "org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping";
private static final String SOURCE = "REST";

/**
* Handle all the exceptions inside REST project.
*
* @param ex      {@link Exception} occurs inside REST project
* @param request Incoming request to REST project.
* @param servReq Incoming request to REST project
* @return response of type {@link ResponseEntity}.
*/
@ExceptionHandler({ Exception.class })
public ResponseEntity handleAll(final Exception ex, final WebRequest request,
final HttpServletRequest servReq) {
logger.info(ex.getClass().getName());
logger.error("error", ex);
CustomException customException = null;
if (ex instanceof CustomException) {
customException = (CustomException) ex;
} else {
throw new IllegalStateException(HttpStatus.INTERNAL_SERVER_ERROR.name());
}

final ApiError apiError = ApiError.builder().status(HttpStatus.INTERNAL_SERVER_ERROR)
.code(customException.getCode()).message(ex.getLocalizedMessage())
.errors(Arrays.asList("error occurred")).path(servReq.getAttribute(REQUEST_PATH).toString())
.source(SOURCE).timeStamp(new Date()).build();

return new ResponseEntity(apiError, new HttpHeaders(), apiError.getStatus());
}
}




Sunday, January 6, 2019

Eclipse and STS build error importing Gradle project

Issue


FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'warehouse'.
> Could not resolve all files for configuration ':classpath'.
   > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:2.1.1.RELEASE.
     Required by:
         project :
      > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:2.1.1.RELEASE.
         > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-gradle-plugin/2.1.1.RELEASE/spring-boot-gradle-plugin-2.1.1.RELEASE.pom
            > Could not resolve org.springframework.boot:spring-boot-tools:2.1.1.RELEASE.
               > Could not resolve org.springframework.boot:spring-boot-tools:2.1.1.RELEASE.
                  > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-tools/2.1.1.RELEASE/spring-boot-tools-2.1.1.RELEASE.pom
                     > Could not resolve org.springframework.boot:spring-boot-parent:2.1.1.RELEASE.
                        > Could not resolve org.springframework.boot:spring-boot-parent:2.1.1.RELEASE.
                           > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-parent/2.1.1.RELEASE/spring-boot-parent-2.1.1.RELEASE.pom
                              > Could not resolve org.springframework.boot:spring-boot-dependencies:2.1.1.RELEASE.
                                 > Could not resolve org.springframework.boot:spring-boot-dependencies:2.1.1.RELEASE.
                                    > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-dependencies/2.1.1.RELEASE/spring-boot-dependencies-2.1.1.RELEASE.pom
                                       > Could not resolve org.springframework.data:spring-data-releasetrain:Lovelace-SR3.
                                          > Could not resolve org.springframework.data:spring-data-releasetrain:Lovelace-SR3.
                                             > Could not get resource 'https://jcenter.bintray.com/org/springframework/data/spring-data-releasetrain/Lovelace-SR3/spring-data-releasetrain-Lovelace-SR3.pom'.
                                                > Could not HEAD 'https://jcenter.bintray.com/org/springframework/data/spring-data-releasetrain/Lovelace-SR3/spring-data-releasetrain-Lovelace-SR3.pom'.
                                                   > sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

CONFIGURE FAILED in 26s



Solution

Set Gradle installation directory to STS - Spring Tool Suite

"Window" --> "Preferences"  ( Go to "Preferences" in "Window" menu )
Select "Gradle" ( If cannot find , type 'gradle' in top left corner search tab
Add Local Installation directory -> your gradle installation directory

Friday, January 4, 2019

Model Mapper Issues



public class ObjectMapperUtils {

    private static ModelMapper modelMapper = new ModelMapper();

    /**
     * Model mapper property setting are specified in the following block.
     * Default property matching strategy is set to Strict see {@link MatchingStrategies}
     * Custom mappings are added using {@link ModelMapper#addMappings(PropertyMap)}
     */
    static {
        modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
    }

    /**
     * Hide from public usage.
     */
    private ObjectMapperUtils() {
    }

    /**
     * Note: outClass object must have default constructor with no arguments
* * @param type of result object. * @param type of source object to map from. * @param entity entity that needs to be mapped. * @param outClass class of result object. * @return new object of outClass type. */
public static <D, T> D map(final T entity, Class<D> outClass) { return modelMapper.map(entity, outClass); } /** * Note: outClass object must have default constructor with no arguments
* * @param entityList list of entities that needs to be mapped * @param outCLass class of result list element * @param type of objects in result list * @param type of entity in entityList * @return list of mapped object with type. */
public static <D, T> List<D> mapAll(final Collection<T> entityList, Class<D> outCLass) { return entityList.stream() .map(entity -> map(entity, outCLass)) .collect(Collectors.toList()); } /** * Maps {@code source} to {@code destination}. * * @param source object to map from * @param destination object to map to */ public static <S, D> D map(final S source, D destination) { modelMapper.map(source, destination); return destination; } }