<chapter id="Validation">
   <title>Validation</title>
   
   <para>
   RESTEasy provides the support for validation mandated by the
   <ulink url="http://www.jcp.org/en/jsr/detail?id=339">JAX-RS: Java API for RESTful Web Services 2.1 </ulink>,
   given the presence of an implementation of the
   <ulink url="https://beanvalidation.org/2.0/spec/">Bean Validation specification</ulink>
   such as <ulink url="http://hibernate.org/validator/">Hibernate Validator</ulink>. 
   </para>
   
   <para>
   Validation provides a declarative way of imposing constraints on fields and properties of beans, bean classes,
   and the parameters and return values of bean methods.  For example, in
   </para>
   
   <programlisting><![CDATA[
@Path("all")
@TestClassConstraint(5)
public class TestResource
{
   @Size(min=2, max=4)
   @PathParam("s")
   String s;

   private String t;

   @Size(min=3)  
   public String getT()
   {
      return t;
   }

   @PathParam("t") 
   public void setT(String t)
   {
      this.t = t;
   }

   @POST
   @Path("{s}/{t}/{u}")
   @Pattern(regexp="[a-c]+")
   public String post(@PathParam("u") String u)
   {
      return u;
   }
}
]]></programlisting>  
   
   <para>
   the field <code>s</code> is constrained by the Bean Validation built-in annotation <code>@Size</code>
   to have between 2 and 4 characters, the property <code>t</code> is constrained to have at least 3
   characters, and the <code>TestResource</code> object is constrained by the application defined
   annotation <code>@TestClassConstraint</code> to have the combined lengths of <code>s</code> and
   <code>t</code> less than 5:
   </para>
   
   <programlisting><![CDATA[
@Constraint(validatedBy = TestClassValidator.class)
@Target({TYPE})
@Retention(RUNTIME)
public @interface TestClassConstraint
{
   String message() default "Concatenation of s and t must have length > {value}";
   Class<?>[] groups() default {};
   Class<? extends Payload>[] payload() default {};
   int value();
}

public class TestClassValidator implements ConstraintValidator<TestClassConstraint, TestResource>
{
   int length;

   public void initialize(TestClassConstraint constraintAnnotation)
   {
      length = constraintAnnotation.value();
   }

   public boolean isValid(TestResource value, ConstraintValidatorContext context)
   {
      boolean b = value.retrieveS().length() + value.getT().length() < length;
   }
}
]]></programlisting> 
   
   <para>
   See the links above for more about how to create validation annotations.
   </para>
   
   <para>
   Also, the method parameter <code>u</code> is constrained to have no more than 5 characters,
   and the return value of method <code>post</code> is constrained by the built-in annotation
   <code>@Pattern</code> to match the regular expression "[a-c]+".
   </para>
   
   <para>
   The sequence of validation constraint testing is as follows:
   </para>
   
   <orderedlist>
   <listitem>Create the resource and validate field, property, and class constraints.</listitem>
   <listitem>Validate the resource method parameters.</listitem>
   <listitem>If no violations have been detected, call the resource method and validate the return value</listitem>
   </orderedlist>
   
   <section>
   <title>Violation reporting</title>
   
   <para>
   If a validation problem occurs, either a problem with the validation definitions or a constraint violation,
   RESTEasy will set the return header <classname>org.jboss.resteasy.api.validation.Validation.VALIDATION_HEADER</classname>
   ("validation-exception") to "true".
   </para>
   
   <para>
   If RESTEasy detects a structural validation problem, such as a validation annotation with a 
   missing validator class, it will return a String representation of a 
   <classname>javax.validation.ValidationException</classname>.  For example
   </para>
   
<programlisting><![CDATA[
javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.[org.jboss.resteasy.test.validation.TestValidationExceptions$OtherValidationException]
]]></programlisting>
   
   <para>
   If any constraint violations are detected, RESTEasy will return a report in one of a variety
   of formats.  If one of "application/xml" or "application/json" occur in the "Accept" request header,
   RESTEasy will return an appropriately marshalled instance of
   <classname>org.jboss.resteasy.api.validation.ViolationReport</classname>:
   </para>

<programlisting><![CDATA[
@XmlRootElement(name="violationReport")
@XmlAccessorType(XmlAccessType.FIELD)
public class ViolationReport
{
   ...

   public ArrayList<ResteasyConstraintViolation> getFieldViolations()
   {
      return fieldViolations;
   }

   public ArrayList<ResteasyConstraintViolation> getPropertyViolations()
   {
      return propertyViolations;
   }

   public ArrayList<ResteasyConstraintViolation> getClassViolations()
   {
      return classViolations;
   }

   public ArrayList<ResteasyConstraintViolation> getParameterViolations()
   {
      return parameterViolations;
   }

   public ArrayList<ResteasyConstraintViolation> getReturnValueViolations()
   {
      return returnValueViolations;
   }

   ...
}
]]></programlisting>

   <para>
   where <classname>org.jboss.resteasy.api.validation.ResteasyConstraintViolation</classname> is defined:
   </para>
   
<programlisting><![CDATA[
@XmlRootElement(name="resteasyConstraintViolation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ResteasyConstraintViolation implements Serializable
{
   ...
   
   /**
    * @return type of constraint
    */
   public ConstraintType.Type getConstraintType()
   {
      return constraintType;
   }
   
   /**
    * @return description of element violating constraint
    */
   public String getPath()
   {
      return path;
   }
   
   /**
    * @return description of constraint violation
    */
   public String getMessage()
   {
      return message;
   }
   
   /**
    * @return object in violation of constraint
    */
   public String getValue()
   {
      return value;
   }
   
   /**
    * @return String representation of violation
    */
   public String toString()
   {
      return "[" + type() + "]\r[" + path + "]\r[" + message + "]\r[" + value + "]\r";
   }
   
   /**
    * @return String form of violation type 
    */
   public String type()
   {
      return constraintType.toString();
   }
}
]]></programlisting>

   <para>
   and <classname>org.jboss.resteasy.api.validation.ConstraintType</classname> is the enumeration
   </para>
   
<programlisting><![CDATA[
public class ConstraintType
{
   public enum Type {CLASS, FIELD, PROPERTY, PARAMETER, RETURN_VALUE};
}
]]></programlisting>

   <para>
   If both "application/xml" or "application/json" occur in the "Accept" request header, the media type
   is chosen according to the ranking given by implicit or explicit "q" parameter values.  In the
   case of a tie, the returned media type is indeterminate.
   </para>
   
   <para>If neither "application/xml" or "application/json" occur in the "Accept" request header,
   RESTEasy returns a report with a String representation of each
   <classname>ResteasyConstraintViolation</classname>, where
   each field is delimited by '[' and ']', followed by a '\r', with a final '\r' at the end.
   For example,
   </para>
   
<programlisting><![CDATA[
[FIELD]
[s]
[size must be between 2 and 4]
[a]

[PROPERTY]
[t]
[size must be between 3 and 5]
[z]

[CLASS]
[]
[Concatenation of s and t must have length > 5]
[org.jboss.resteasy.validation.TestResource@68467a6f]

[PARAMETER]
[test.<cross-parameter>]
[Parameters must total <= 7]
[[5, 7]]

[RETURN_VALUE]
[g.<return value>]
[size must be between 2 and 4]
[abcde]
]]></programlisting>

   <para>
   where the four fields are
   </para>
   
   <orderedlist>
      <listitem>type of constraint</listitem>
      <listitem>path to violating element (e.g., field name, class name, method name and parameter name)</listitem>
      <listitem>message</listitem>
      <listitem>violating element</listitem>
   </orderedlist>
   
   <para>
   The <classname>ViolationReport</classname> can be reconsititued from the <classname>String</classname> as follows:
   </para>
   
<programlisting><![CDATA[
ResteasyClient client = new ResteasyClientBuilder().build();
Invocation.Builder request = client.target(...).request();
Response response = request.get();
if (Boolean.valueOf(response.getHeaders().getFirst(Validation.VALIDATION_HEADER)))
{
   String s = response.getEntity(String.class);
   ViolationReport report = new ViolationReport(s);
}
]]></programlisting>

<para>
If the path field is considered to be too much server side information, it can be surpressed by setting
the parameter "resteasy.validation.suppress.path" to "true". In that case, "*" will be returned in the
path fields. [See <xref linkend="config_mechanisms"/> for more information about application configuration.]
</para>

   </section>
            
   <section>
         <title>Validation Service Providers</title>
         
            <para>
   The form of validation mandated by the JAX-RS 2.1 specification, based on Bean Validation 1.1 or greater,
   is supported by the RESTEasy module resteasy-validator-provider, which produces the artifact
   resteasy-validator-provider-&lt;version&gt;.jar.  Validation is turned on by default (assuming
   resteasy-validator-provider-&lt;version&gt;.jar is available), though
   parameter and return value validation can be turned off or modified in the validation.xml
   configuration file. See the
   <ulink url="https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/?v=6.0">Hibernate Validator</ulink>
   documentation for the details.
   </para>
   
         <para>
         RESTEasy obtains a bean validation implemenation by looking in the available
         META-INF/services/javax.ws.rs.Providers files for an implementation
         of <classname>ContextResolver&lt;GeneralValidator&gt;</classname>, where 
         <classname>org.jboss.resteasy.spi.GeneralValidator</classname> is
         </para>

<programlisting><![CDATA[
public interface GeneralValidator
{
   /**
    * Validates all constraints on {@code object}.
    *
    * @param object object to validate
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return constraint violations or an empty set if none
    * @throws IllegalArgumentException if object is {@code null}
    *         or if {@code null} is passed to the varargs groups
    * @throws ValidationException if a non recoverable error happens
    *         during the validation process
    */
   public abstract void validate(HttpRequest request, Object object, Class<?>... groups);
   /**
    * Validates all constraints placed on the parameters of the given method.
    *
    * @param <T> the type hosting the method to validate
    * @param object the object on which the method to validate is invoked
    * @param method the method for which the parameter constraints is validated
    * @param parameterValues the values provided by the caller for the given method's
    *        parameters
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return a set with the constraint violations caused by this validation;
    *         will be empty if no error occurs, but never {@code null}
    * @throws IllegalArgumentException if {@code null} is passed for any of the parameters
    *         or if parameters don't match with each other
    * @throws ValidationException if a non recoverable error happens during the
    *         validation process
    */
   public abstract void validateAllParameters(HttpRequest request, Object object, Method method, Object[] parameterValues, Class<?>... groups);

   /**
    * Validates all return value constraints of the given method.
    *
    * @param <T> the type hosting the method to validate
    * @param object the object on which the method to validate is invoked
    * @param method the method for which the return value constraints is validated
    * @param returnValue the value returned by the given method
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return a set with the constraint violations caused by this validation;
    *         will be empty if no error occurs, but never {@code null}
    * @throws IllegalArgumentException if {@code null} is passed for any of the object,
    *         method or groups parameters or if parameters don't match with each other
    * @throws ValidationException if a non recoverable error happens during the
    *         validation process
    */
   public abstract void validateReturnValue(
         HttpRequest request, Object object, Method method, Object returnValue, Class<?>... groups);

   /**
    * Indicates if validation is turned on for a class.
    * 
    * @param clazz Class to be examined
    * @return true if and only if validation is turned on for clazz
    */
   public abstract boolean isValidatable(Class<?> clazz);
     
   /**
    * Indicates if validation is turned on for a method.
    * 
    * @param method method to be examined
    * @return true if and only if validation is turned on for method
    */   
   public abstract boolean isMethodValidatable(Method method);

   void checkViolations(HttpRequest request);
}
]]></programlisting>

   <para>
   The methods and the javadoc are adapted from the Bean Validation 1.1 classes
   <classname>javax.validation.Validator</classname> and
   <classname>javax.validation.executable.ExecutableValidator</classname>.
   </para>
 
   <para>
   RESTEasy module resteasy-validator-provider supplies an implementation
   of <methodname>GeneralValidator</methodname>.
   An alternative implementation may be supplied by implementing 
   <classname>ContextResolver&lt;GeneralValidator&gt;</classname> and
   <classname>org.jboss.resteasy.spi.validation.GeneralValidator</classname>.
   </para>
   
   <para>
   A validator intended to function in the presence of CDI must also implement the subinterface
   </para>
   
   <programlisting><![CDATA[
public interface GeneralValidatorCDI extends GeneralValidator
{
   /**
    * Indicates if validation is turned on for a class.
    * 
    * This method should be called from the resteasy-jaxrs module. It should
    * test if injectorFactor is an instance of CdiInjectorFactory, which indicates
    * that CDI is active.  If so, it should return false. Otherwise, it should
    * return the same value returned by GeneralValidator.isValidatable().
    * 
    * @param clazz Class to be examined
    * @param injectorFactory the InjectorFactory used for clazz
    * @return true if and only if validation is turned on for clazz
    */
   public boolean isValidatable(Class<?> clazz, InjectorFactory injectorFactory);
   
   /**
    * Indicates if validation is turned on for a class.
    * This method should be called only from the resteasy-cdi module.
    * 
    * @param clazz Class to be examined
    * @return true if and only if validation is turned on for clazz
    */
   public abstract boolean isValidatableFromCDI(Class<?> clazz);
  
   /**
    * Throws a ResteasyViolationException if any validation violations have been detected.
    * The method should be called only from the resteasy-cdi module.
    * @param request
    */
   public void checkViolationsfromCDI(HttpRequest request);
   
   /**
    * Throws a ResteasyViolationException if either a ConstraintViolationException or a
    * ResteasyConstraintViolationException is embedded in the cause hierarchy of e.
    * 
    * @param request
    * @param e
    */
   public void checkForConstraintViolations(HttpRequest request, Exception e);
}
   ]]></programlisting>

   <para>
   The validator in resteasy-validator-provider implements GeneralValidatorCDI.
   </para>
   </section>
   
   <section>
      <title>Validation Implementations</title>
         
    <para>
    As mentioned above, RESTEasy validation requires an implementation of the
    <ulink url="https://beanvalidation.org/2.0/spec/">Bean Validation specification</ulink>
    such as <ulink url="http://hibernate.org/validator/">Hibernate Validator</ulink>. Hibernate
    Validator is supplied automatically when RESTEasy is running in the context of WildFly. Otherwise,
    it should be made available. For example, in maven
    
    <programlisting>
        &lt;dependency&gt;
            &lt;groupId&gt;org.hibernate.validator&lt;/groupId&gt;
            &lt;artifactId&gt;hibernate-validator/artifactId&gt;
        &lt;/dependency&gt;
    </programlisting>
    </para>
   </section>
</chapter>
