Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-2795

ResteasyViolationException discard custom ConstraintViolationException message

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: Major Major
    • None
    • 3.12.1.Final, 3.14.0.Final, 4.6.0.Final
    • None
    • None
    • Hide
      1. Using Wildfly 21.0.0.Final (RestEasy 3.12.1.Final)
      2. Create a custom JAX-RS "ConstraintViolationException" "ExceptionMapper"
      3. Throw a custom "ConstraintViolationException" with given "message" and "Set" of "ConstraintViolations"

      Observes: JAX-RS "ContraintViolationException" "ExceptionMapper" receive a ResteasyViolationException which has discarded the original message.

      Show
      Using Wildfly 21.0.0.Final (RestEasy 3.12.1.Final) Create a custom JAX-RS "ConstraintViolationException" "ExceptionMapper" Throw a custom "ConstraintViolationException" with given "message" and "Set" of "ConstraintViolations" Observes: JAX-RS "ContraintViolationException" "ExceptionMapper" receive a ResteasyViolationException which has discarded the original message.
    • Undefined

      In our application, we are using ConstraintViolationException as a single source of exception to all our business validation.

       

      Our application throw custom ConstraintViolationException using the standard annotations mechanism or programmatically like this:

      Set<ConstraintViolation<?>> violations = validate(...);
      if (!violations.isEmpty()) {
          throw new ConstraintViolationException("custom message here", violations);
      }
      

       

      To return theses exceptions through our REST API (either thrown by the standard annotations or programmatically), we have created a "ConstraintViolationException" "ExceptionMapper" such as:

       

      @Provider
      public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
      
      
          @Override
          public Response toResponse(ConstraintViolationException exception) {
              VndErrors vndErrors = mapConstraintViolationExceptionToVndErrors(exception);
              return Response
                      .status(Status.BAD_REQUEST)
                      .type("application/vnd.errors+json")
                      .entity(vndErrors)
                      .build();
          }
      
          public VndErrors mapConstraintViolationExceptionToVndErrors(ConstraintViolationException exception) {
              String rootMessage = exception.getMessage();
              Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
              // ... custom mapping
          }

       

       

      The issue is that after throwing our custom constraint exception, the actual implementation of "ConstraintViolationException" we receive through the "ExceptionMapper" i.e. RestEasy "ResteasyViolationException", and that actual implementation has lost track of the "custom validation message".

       

      I have looked a bit at the source code to find the issue and issue seem caused by RestEasy relying on the following "ConstraintViolationException" constructor

      	/**
      	 * Creates a constraint violation report.
      	 *
      	 * @param constraintViolations a {@code Set} of {@link ConstraintViolation}s or null
      	 */
      	public ConstraintViolationException(Set<? extends ConstraintViolation<?>> constraintViolations) {
      		this(
      				constraintViolations != null ? toString( constraintViolations ) : null,
      				constraintViolations
      		);
      	}
      

      instead of the following "ConstraintViolationException" constructor

      	/**
      	 * Creates a constraint violation report.
      	 *
      	 * @param message error message
      	 * @param constraintViolations a {@code Set} of {@link ConstraintViolation}s or null
      	 */
      	public ConstraintViolationException(String message, Set<? extends ConstraintViolation<?>> constraintViolations) {
      		super( message );
      
      
      		if ( constraintViolations == null ) {
      			this.constraintViolations = null;
      		}
      		else {
      			this.constraintViolations = new HashSet<>( constraintViolations );
      		}
      	}
      

      I'm not sure how the issue should be addressed but my guess is that both RestEasy "ResteasyViolationException" and "GeneralValidatorImpl" classes need some adaptations to actually keep trace of any custom validation message that has been set previously.

       

      If you think appropriate, and given some further implementation guidance, I can volunteer for a PR to resolve this issue in the 3.12 branch (or the one that will be consumed by Wildfly 22) and master.

       

      Thanks!

              rsigal@redhat.com Ronald Sigal
              mathieu@mathieulachance.com Mathieu Lachance (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: