DatatableExceptionHandlerV2.java
package sk.iway.iwcm.system.spring;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ResponseStatusException;
import sk.iway.iwcm.Adminlog;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.system.datatable.Datatable;
import sk.iway.iwcm.system.datatable.DatatableFieldError;
import sk.iway.iwcm.system.datatable.DatatableResponse;
import sk.iway.iwcm.system.datatable.DatatableRestControllerV2;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Title webjet8
* Company Interway a. s. (www.interway.sk)
* Copyright Interway a. s. (c) 2001-2019
* @author tmarcinkova $
* @created 2019/05/10 11:05
*
* Tato trieda spracuje vynimku a vrati response pre DataTable Editor
* - stara sa len o vynimky z tried, ktore maju anotaciu '@Datatable'
*
*/
@ControllerAdvice(annotations = {Datatable.class})
public class DatatableExceptionHandlerV2
{
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<DatatableResponse<Object>> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
DatatableResponse<Object> response = new DatatableResponse<>();
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
List<ObjectError> globalErrors = ex.getBindingResult().getGlobalErrors();
if (!fieldErrors.isEmpty()) {
response.setFieldErrors(fieldErrors.stream().map(e -> new DatatableFieldError(getField(e.getField()), e.getDefaultMessage())).collect(Collectors.toList()));
}
if (!globalErrors.isEmpty()) {
response.setError(globalErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(", ")));
}
if (Tools.isEmpty(response.getError())) {
//vyhod este globalnu error hlasku, aby sa zobrazila aj pri tlacitkach a user si preklikal taby na konkretne chyby
response.setError(Prop.getInstance().getText("datatable.error.fieldErrorMessage"));
}
return ResponseEntity.ok(response);
}
private String getField(String str) {
String result = str;
if (str.contains(".")) {
result = str.substring(str.indexOf(".") + 1);
}
return result;
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<DatatableResponse<Object>> handleException(ConstraintViolationException ex) {
DatatableResponse<Object> response = new DatatableResponse<>();
List<DatatableFieldError> errorsList = new ArrayList<>();
if (ex.getConstraintViolations()!=null && ex.getConstraintViolations().isEmpty()==false) {
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
String propertyName = violation.getPropertyPath().toString();
int dot = propertyName.indexOf(".");
//toto potrebujeme kvoli @Valid anotacii na nested properties, ktore su konvertovane, priklad je GroupsApproveEntity.group kde nam hadze chybu na group.navbarName ale DT ma definovany len field group
if (dot > 0 && propertyName.startsWith("editorFields")==false) propertyName = propertyName.substring(0, dot);
errorsList.add(new DatatableFieldError(propertyName, getErrorMessage(violation)));
}
response.setFieldErrors(errorsList);
} else {
response.setError(ex.getMessage());
Logger.error(DatatableExceptionHandlerV2.class, ex);
}
if (DatatableRestControllerV2.getLastImportedRow()!=null) {
StringBuilder errors = new StringBuilder("");
if (response.getFieldErrors()!=null) {
for (DatatableFieldError error : response.getFieldErrors()) {
errors.append("\n").append(error.getName()).append(" - ").append(error.getStatus());
}
}
if (Tools.isEmpty(response.getError())) {
if (errors.length()<1) errors.append(Prop.getInstance().getText("datatable.error.fieldErrorMessage"));
response.setError(Prop.getInstance().getText("datatable.error.importRow", String.valueOf(DatatableRestControllerV2.getLastImportedRow().intValue()+1), errors.toString()));
} else {
response.setError(Prop.getInstance().getText("datatable.error.importRow", String.valueOf(DatatableRestControllerV2.getLastImportedRow().intValue()+1), response.getError()));
}
}
if (Tools.isEmpty(response.getError())) {
//vyhod este globalnu error hlasku, aby sa zobrazila aj pri tlacitkach a user si preklikal taby na konkretne chyby
response.setError(Prop.getInstance().getText("datatable.error.fieldErrorMessage"));
}
return new ResponseEntity<>(response, null, HttpStatus.OK);
}
@ExceptionHandler(TransactionSystemException.class)
public ResponseEntity<DatatableResponse<Object>> handleException(TransactionSystemException ex) {
DatatableResponse<Object> response = new DatatableResponse<>();
List<DatatableFieldError> errorsList = new ArrayList<>();
Throwable t = ex.getCause();
while ((t != null) && !(t instanceof ConstraintViolationException)) {
t = t.getCause();
}
if (t instanceof ConstraintViolationException) {
// Here you're sure you have a ConstraintViolationException, you can handle it
Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) t).getConstraintViolations();
if (!violations.isEmpty()) {
for (ConstraintViolation<?> violation : violations) {
errorsList.add(new DatatableFieldError(violation.getPropertyPath().toString(), getErrorMessage(violation)));
}
}
response.setFieldErrors(errorsList);
} else if (ex instanceof TransactionSystemException) {
String err = ex.getMessage();
try {
if (err != null) {
int start = err.indexOf("Duplicate entry");
if (start > 0) {
int end = err.indexOf("Error Code", start);
if (end > start) err = err.substring(start, end).trim();
}
}
} catch (Exception e) {
//failsafe
}
response.setError(err);
Logger.error(DatatableExceptionHandlerV2.class, ex);
} else {
response.setError(ex.getMessage());
Logger.error(DatatableExceptionHandlerV2.class, ex);
}
if (Tools.isEmpty(response.getError())) {
//vyhod este globalnu error hlasku, aby sa zobrazila aj pri tlacitkach a user si preklikal taby na konkretne chyby
response.setError(Prop.getInstance().getText("datatable.error.fieldErrorMessage"));
}
return new ResponseEntity<>(response, null, HttpStatus.OK);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<DatatableResponse<Object>> handleException(Exception ex) {
DatatableResponse<Object> response = new DatatableResponse<>();
String message = null;
if (ex instanceof ResponseStatusException) {
ResponseStatusException ex2 = (ResponseStatusException)ex;
message = ex2.getReason();
}
if (Tools.isEmpty(message)) message = ex.getMessage();
if (Tools.isEmpty(message)) message = Prop.getInstance().getText("datatable.error.unknown");
if (message!=null && message.contains("DatabaseException")) {
int i = message.indexOf("Call:");
if (i > 0) {
Adminlog.add(Adminlog.TYPE_SQLERROR, message, -1, -1);
message = message.substring(0, i).trim();
}
}
if (message != null && message.contains("JSON parse error")) {
int i = message.indexOf(", problem:");
if (i > 0) {
Adminlog.add(Adminlog.TYPE_JSPERROR, message, -1, -1);
message = message.substring(0, i).trim();
}
}
response.setError(message);
Logger.error(DatatableExceptionHandlerV2.class, ex);
return new ResponseEntity<>(response, null, HttpStatus.OK);
}
/**
* Pokusi sa ziskat text chyby z WJ prekladov
* @param violation
* @return
*/
private String getErrorMessage(ConstraintViolation<?> violation) {
//{javax.validation.constraints.NotBlank.message}
String key = violation.getMessageTemplate();
if (key != null && key.length()>3) {
if (key.startsWith("{") && key.endsWith("}") && key.length()>3) key = key.substring(1, key.length()-1);
Prop prop = Prop.getInstance();
String message = prop.getText(key);
if (key.equals(message)==false) {
//skus dohladat atributy a nahradit ich
try {
if (violation.getConstraintDescriptor()!=null && violation.getConstraintDescriptor().getAttributes()!=null) {
Map<String, Object> attributes = violation.getConstraintDescriptor().getAttributes();
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
String expression = "{"+entry.getKey()+"}";
if (message.contains(expression)) {
message = Tools.replace(message, expression, String.valueOf(attributes.get(entry.getKey())));
}
}
}
String validatedValue = String.valueOf(violation.getInvalidValue());
message = Tools.replace(message, "${validatedValue}", validatedValue);
} catch (Exception ex) {
//failsafe
}
return message;
}
}
return violation.getMessage();
}
}