Example #1
0
  private boolean isResolvable(ValueReference ref, ValueExpression valueExpression) {
    Boolean result = null;
    String failureMessage = null;

    if (null == valueExpression) {
      failureMessage =
          "Unable to validate expression using Bean " + "Validation.  Expression must not be null.";
      result = false;
    } else if (null == ref) {
      failureMessage =
          "Unable to validate expression "
              + valueExpression.getExpressionString()
              + " using Bean Validation.  Unable to get value of expression.";
      result = false;
    } else {
      Class baseClass = ref.getBaseClass();

      // case 1, base classes of Map, List, or Array are not resolvable
      if (null != baseClass) {
        if (Map.class.isAssignableFrom(baseClass)
            || Collection.class.isAssignableFrom(baseClass)
            || Array.class.isAssignableFrom(baseClass)) {
          failureMessage =
              "Unable to validate expression "
                  + valueExpression.getExpressionString()
                  + " using Bean Validation.  Expression evaluates to a Map, List or array.";
          result = false;
        }
      }
    }

    result = ((null != result) ? result : true);
    if (!result) {
      LOGGER.fine(failureMessage);
    }

    return result;
  }
Example #2
0
  /**
   * <span class="changed_modified_2_3">Verify</span> that the value is valid according to the Bean
   * Validation constraints. <div class="changed_added_2_0">
   *
   * <p>Obtain a {@link ValidatorFactory} instance by calling {@link
   * javax.validation.Validation#buildDefaultValidatorFactory}.
   *
   * <p>Let <em>validationGroupsArray</em> be a <code>Class []</code> representing validator groups
   * set on the component by the tag handler for this validator. The first search component
   * terminates the search for the validation groups value. If no such value is found use the class
   * name of {@link javax.validation.groups.Default} as the value of the validation groups.
   *
   * <p>Let <em>valueExpression</em> be the return from calling {@link
   * UIComponent#getValueExpression} on the argument <em>component</em>, passing the literal string
   * &#8220;value&#8221; (without the quotes) as an argument. If this application is running in an
   * environment with a Unified EL Implementation for Java EE6 or later, obtain the <code>
   * ValueReference</code> from <em>valueExpression</em> and let <em>valueBaseClase</em> be the
   * return from calling <code>ValueReference.getBase()</code> and <em>valueProperty</em> be the
   * return from calling <code>ValueReference.getProperty()</code>. If an earlier version of the
   * Unified EL is present, use the appropriate methods to inspect <em>valueExpression</em> and
   * derive values for <em>valueBaseClass</em> and <em>valueProperty</em>.
   *
   * <p>If no <code>ValueReference</code> can be obtained, take no action and return.
   *
   * <p>If <code>ValueReference.getBase()</code> return <code>null</code>, take no action and
   * return.
   *
   * <p>Obtain the {@link ValidatorContext} from the {@link ValidatorFactory}.
   *
   * <p>Decorate the {@link MessageInterpolator} returned from {@link
   * ValidatorFactory#getMessageInterpolator} with one that leverages the <code>Locale</code>
   * returned from {@link javax.faces.component.UIViewRoot#getLocale}, and store it in the <code>
   * ValidatorContext</code> using {@link ValidatorContext#messageInterpolator}.
   *
   * <p>Obtain the {@link javax.validation.Validator} instance from the <code>validatorContext
   * </code>.
   *
   * <p>Obtain a <code>javax.validation.BeanDescriptor</code> from the <code>
   * javax.validation.Validator</code>. If <code>hasConstraints()</code> on the <code>BeanDescriptor
   * </code> returns false, take no action and return. Otherwise proceed.
   *
   * <p>Call {@link javax.validation.Validator#validateValue}, passing <em>valueBaseClass</em>,
   * <em>valueProperty</em>, the <em>value</em> argument, and <em>validatorGroupsArray</em> as
   * arguments.
   *
   * <p>If the returned <code>Set&lt;{@link
   * ConstraintViolation}&gt;</code> is non-empty, for each element in the <code>Set</code>, create
   * a {@link FacesMessage} where the summary and detail are the return from calling {@link
   * ConstraintViolation#getMessage}. Capture all such <code>FacesMessage</code> instances into a
   * <code>Collection</code> and pass them to {@link
   * ValidatorException#ValidatorException(java.util.Collection)}. <span
   * class="changed_added_2_3">If the {@link #ENABLE_VALIDATE_WHOLE_BEAN_PARAM_NAME} application
   * parameter is enabled and this {@code Validator} instance has validation groups other than or in
   * addition to the {@code Default} group, record the fact that this field failed validation so
   * that any <code>&lt;f:validateWholeBean /&gt;</code> component later in the tree is able to skip
   * class-level validation for the bean for which this particular field is a property. Regardless
   * of whether or not {@link #ENABLE_VALIDATE_WHOLE_BEAN_PARAM_NAME} is set, throw the new
   * exception.</span>
   *
   * <p class="changed_added_2_3">If the returned {@code Set} is empty, the {@link
   * #ENABLE_VALIDATE_WHOLE_BEAN_PARAM_NAME} application parameter is enabled and this {@code
   * Validator} instance has validation groups other than or in addition to the {@code Default}
   * group, record the fact that this field passed validation so that any <code>
   * &lt;f:validateWholeBean /&gt;</code> component later in the tree is able to allow class-level
   * validation for the bean for which this particular field is a property. </div>
   *
   * @param context {@inheritDoc}
   * @param component {@inheritDoc}
   * @param value {@inheritDoc}
   * @throws ValidatorException {@inheritDoc}
   */
  @Override
  public void validate(FacesContext context, UIComponent component, Object value) {

    if (context == null) {
      throw new NullPointerException();
    }
    if (component == null) {
      throw new NullPointerException();
    }
    ValueExpression valueExpression = component.getValueExpression("value");
    if (valueExpression == null) {
      return;
    }

    ValidatorFactory validatorFactory;
    Object cachedObject =
        context.getExternalContext().getApplicationMap().get(VALIDATOR_FACTORY_KEY);
    if (cachedObject instanceof ValidatorFactory) {
      validatorFactory = (ValidatorFactory) cachedObject;
    } else {
      try {
        validatorFactory = Validation.buildDefaultValidatorFactory();
      } catch (ValidationException e) {
        throw new FacesException("Could not build a default Bean Validator factory", e);
      }
      context.getExternalContext().getApplicationMap().put(VALIDATOR_FACTORY_KEY, validatorFactory);
    }

    ValidatorContext validatorContext = validatorFactory.usingContext();
    MessageInterpolator jsfMessageInterpolator =
        new JsfAwareMessageInterpolator(context, validatorFactory.getMessageInterpolator());
    validatorContext.messageInterpolator(jsfMessageInterpolator);
    javax.validation.Validator beanValidator = validatorContext.getValidator();
    Class[] validationGroupsArray = parseValidationGroups(getValidationGroups());

    // PENDING(rlubke, driscoll): When EL 1.3 is present, we won't need
    // this.

    ValueExpressionAnalyzer expressionAnalyzer = new ValueExpressionAnalyzer(valueExpression);

    ValueReference valueReference = expressionAnalyzer.getReference(context.getELContext());
    if (valueReference == null) {
      return;
    }
    if (isResolvable(valueReference, valueExpression)) {
      Set<ConstraintViolation<?>> violations = null;
      try {
        //noinspection unchecked
        violations =
            beanValidator.validateValue(
                valueReference.getBaseClass(),
                valueReference.getProperty(),
                value,
                validationGroupsArray);
      } catch (IllegalArgumentException iae) {
        String failureMessage =
            "Unable to validate expression "
                + valueExpression.getExpressionString()
                + " using Bean Validation.  Unable to get value of expression. "
                + " Message from Bean Validation: "
                + iae.getMessage();
        LOGGER.fine(failureMessage);
      }

      if (violations != null && !violations.isEmpty()) {
        ValidatorException toThrow;
        if (1 == violations.size()) {
          ConstraintViolation violation = violations.iterator().next();
          toThrow =
              new ValidatorException(
                  MessageFactory.getMessage(
                      context,
                      MESSAGE_ID,
                      violation.getMessage(),
                      MessageFactory.getLabel(context, component)));
        } else {
          Set<FacesMessage> messages = new LinkedHashSet<>(violations.size());
          for (ConstraintViolation violation : violations) {
            messages.add(
                MessageFactory.getMessage(
                    context,
                    MESSAGE_ID,
                    violation.getMessage(),
                    MessageFactory.getLabel(context, component)));
          }
          toThrow = new ValidatorException(messages);
        }

        // Record the fact that this field failed validation, so that multi-field
        // validation is not attempted.
        if (MultiFieldValidationUtils.wholeBeanValidationEnabled(context, validationGroupsArray)) {
          Map<Object, Map<String, Map<String, Object>>> multiFieldCandidates =
              MultiFieldValidationUtils.getMultiFieldValidationCandidates(context, true);
          Object val = valueReference.getBase();
          Map<String, Map<String, Object>> candidate =
              multiFieldCandidates.getOrDefault(val, new HashMap<>());
          Map<String, Object> tuple = new HashMap<>();
          tuple.put("component", component);
          tuple.put("value", FAILED_FIELD_LEVEL_VALIDATION);
          candidate.put(valueReference.getProperty(), tuple);
          multiFieldCandidates.putIfAbsent(val, candidate);
        }

        throw toThrow;
      }
    }

    // Record the fact that this field passed validation, so that multi-field
    // validation can be performed if desired
    if (MultiFieldValidationUtils.wholeBeanValidationEnabled(context, validationGroupsArray)) {
      Map<Object, Map<String, Map<String, Object>>> multiFieldCandidates =
          MultiFieldValidationUtils.getMultiFieldValidationCandidates(context, true);
      Object val = valueReference.getBase();
      Map<String, Map<String, Object>> candidate =
          multiFieldCandidates.getOrDefault(val, new HashMap<>());
      Map<String, Object> tuple =
          new HashMap<>(); // new ComponentValueTuple((EditableValueHolder) component, value);
      tuple.put("component", component);
      tuple.put("value", value);
      candidate.put(valueReference.getProperty(), tuple);
      multiFieldCandidates.putIfAbsent(val, candidate);
    }
  }