001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.server.validation; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022 023import org.apache.juneau.http.response.*; 024 025/** 026 * Specialization of {@link BadRequest} thrown by {@link BeanValidator} when one or more Jakarta Bean Validation 027 * constraints on a {@code @Content}, {@code @FormData}, or {@code @Request}-bound parameter are violated. 028 * 029 * <p> 030 * Behaves like a plain {@code 400 Bad Request} from the wire's perspective — same status code, same reason 031 * phrase, same default JSON envelope shape used by the rest of the framework's exception path — but adds a 032 * structured, serialized {@link #getViolations() violations list} so the response body can include the list of 033 * field-level errors that produced the failure. 034 * 035 * <h5 class='section'>Wire shape:</h5> 036 * <p> 037 * The response body depends on whether RFC 7807 problem-details mode is enabled on the resource: 038 * <ul class='spaced-list'> 039 * <li><b>Problem-details ON</b> — {@code application/problem+json} with the standard {@code status} / 040 * {@code title} / {@code detail} members plus an {@code errors[]} extension array populated from 041 * {@link #getViolations()}. 042 * <li><b>Problem-details OFF (default)</b> — {@code application/json} with a simple 043 * {@code { "status":400, "errors":[ ... ] }} envelope. 044 * </ul> 045 * 046 * <h5 class='section'>See Also:</h5><ul> 047 * <li class='jc'>{@link ValidationViolation} 048 * <li class='jc'>{@link BeanValidator} 049 * <li class='jc'>{@link BadRequest} 050 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerValidation">Jakarta Bean Validation</a> 051 * </ul> 052 * 053 * @since 10.0.0 054 */ 055@SuppressWarnings({ 056 "java:S110", // Deep inheritance inherent to the exception hierarchy 057 "java:S2160" // Exceptions are compared by identity, not by their violations payload; no equals() override needed 058}) 059public class ValidationException extends BadRequest { 060 061 private static final long serialVersionUID = 1L; 062 063 /** The default detail message used when constructed without an explicit message. */ 064 public static final String DEFAULT_MESSAGE = "Request validation failed"; 065 066 private final transient List<ValidationViolation> violations; 067 068 /** 069 * Constructor. 070 * 071 * @param violations The list of constraint violations. Must not be <jk>null</jk> (use an empty list to signal 072 * "validation requested but no violations" — though that case never actually throws in practice). 073 */ 074 public ValidationException(List<ValidationViolation> violations) { 075 super(DEFAULT_MESSAGE); 076 this.violations = List.copyOf(violations); 077 } 078 079 /** 080 * Constructor with a custom detail message. 081 * 082 * @param violations The list of constraint violations. Must not be <jk>null</jk>. 083 * @param msg The detail message. May be <jk>null</jk>. 084 * Treated as a format pattern when {@code args} is non-empty. 085 * @param args Optional message arguments. 086 */ 087 public ValidationException(List<ValidationViolation> violations, String msg, Object...args) { 088 super(msg, args); 089 this.violations = List.copyOf(violations); 090 } 091 092 /** 093 * Returns the immutable list of constraint violations that produced this exception. 094 * 095 * @return The list of violations, never <jk>null</jk> and never modifiable. 096 */ 097 public List<ValidationViolation> getViolations() { 098 return violations; 099 } 100 101 /** 102 * Returns a defensive, mutable copy of the violation list for callers that need to inspect / filter it 103 * without mutating the exception's own state. 104 * 105 * @return A new mutable copy. Never <jk>null</jk>. 106 */ 107 public List<ValidationViolation> copyViolations() { 108 return cp(violations); 109 } 110}