1

In my Spring Boot project, I have a POJO class for reading yaml config file.

@Configuration
@ConfigurationProperties("filenet")
@Data
public class ApplicationConfig {
    @NotNull(message = "Input directory cannot be null")
    @NotBlank(message = "Input directory cannot be blank")
    private String **inputDir**;
    @NotNull(message = "Working directory cannot be null")
    @NotBlank(message = "Working directory cannot be blank")
    private String **workingDir**;
    @Pattern(regexp = "[0-9]+",message = "Invalid value for SMTP port")
    private String port;
}

Sometimes it happens that either inputDir or workingDir or other fields of the config file are blank. I'm using javax.validation.constraints to check for blank or null. When so, and when application is started, I see an exception message and program is terminated. ex: Port has validation that it has to be a number. What I would like to do is to gracefully handle this exception and send an email to concerned team to take care of it.

I have created a class where I'm validating the contents of config file

@Component
public class ConfigParamValidation {

    public List<String>  validateApplicationConfigFile(ApplicationConfig applicationConfig) {

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<ApplicationConfig.ContentEngine>> contentEngineVoilations = validator.validate(applicationConfig.contentEngine);

        exceptionMessgae = contentEngineVoilations.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());

        if(!exceptionMessgae.isEmpty()) {
            through new ConfigFileException(<by passing all required params>);
        }
    }
}

I have tried to create a class which extends RuntimeException

public class ConfigFileException extends RuntimeException {

    private String message;
    private List<String> details;
    private String hint;
    private String nextActions;
    private String support;
    private HttpStatus httpStatus;
    private ZonedDateTime zonedDateTime;

    public ConfigFileException(String message) {
        super(message);
    }

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

    public ConfigFileException(String message, Throwable cause, List<String> details, String hint, String nextActions, String support, HttpStatus httpStatus, ZonedDateTime zonedDateTime) {
        super(message, cause);

        this.message = message;
        this.details=details;
        this.hint=hint;
        this.nextActions=nextActions;
        this.support=support;
        this.httpStatus=httpStatus;
        this.zonedDateTime = zonedDateTime;
    }
}

Another class with @ExceptionHandler

@Data
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SupportTeamException {

    @ExceptionHandler(value = {ConfigFileException.class})
    public ResponseEntity<Object> handleConfigFileException(ConfigFileException e) {
        ConfigFileException configFileException = new ConfigFileException(e.getMessage(), e, e.getDetails(), e.getHint(), e.getNextActions(), e.getSupport(), e.getHttpStatus(), e.getZonedDateTime());
        return new ResponseEntity<Object>(configFileException,e.getHttpStatus());
    }
}

The problem is that for some reason control is not passed to my SupportTeamException class where I could send email.

Or is there a better way to handle this?

1 Answer 1

1

It was my understanding that @ControllerAdvice only works for components that are annotated with @Controller or @RestController.

Since the validation happens at start up of your spring boot app (and not as a result of a http request), it will never go into it. What you could do is create a Component with a @PostConstructor method. (See below) I would strongly recommend to inject your Validator rather than building it yourself (to utilise Spring Boot's full potential).

What I don't fully understand is why you would want to handle this exception gracefully. If your application starts without required properties, it will just fail further down the line when the application actually uses the property. If depending on circumstances (like the environment), certain properties don't need to be there, I would recommend using Validation groups

Finally, small aside @NotBlank will also check it's not null. You don't need both annotations, unless you want to be really specific with your messages.

package com.yourpackage;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.Set;

import static java.util.stream.Collectors.joining;

@Component
public class PropertiesValidator {

    private final Validator validator;
    public final YourProperties properties;

    public PropertiesValidator(final Validator validator, final YourProperties properties) {
        this.validator = validator;
        this.properties = properties;
    }

    @PostConstruct
    public void propertiesShouldBeValid() {
        final Set<ConstraintViolation<YourProperties>> constraintViolations = validator.validate(properties);
        if (!constraintViolations.isEmpty()) {
            final String message = constraintViolations.stream()
                .map(ConstraintViolation::getMessage)
                .collect(joining(","));
            throw new ValidationException(message); //Or send your email
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.