Contact Us 1-800-596-4880

Building a Custom Validator

If you want to perform complex validation logic, or reuse some code you already have on a separate JAR file, then the is-true and is-false fallback validators might not be the most convenient solution for you. You might then want to build your own validator. Leveraging the Validator API, you can create your own validators, and there then are two mechanisms that allow you to execute them in a flow. Below are the necessary steps necessary to do that.

Implement the Validator Interface

Just like other components like the MessageProcessor, Filter, Router, etc., the validators also have a well defined contract inside Mule’s code:

/**
 * A component which performs a validation and expresses
 * its result through a {@link ValidationResult} object.
 * <p/>
 * The {@link #validate(MuleEvent)} method receives a
 * {@link MuleEvent} in order to make it generic and easy to extend.
 * However, that doesn't necessarily mean that all the validation
 * is performed with or over the event exclusively. Thread
 * safeness is not to be assumed over instances of this class
 * since the validator could be stateful.
 *
 * @since 3.7.0
 */
public interface Validator
{

    /**
     * Performs the validation and generates a
     * {@link ValidationResult} back.
     *
     * @param event the current {@link MuleEvent}
     * @return a {@link ValidationResult}
     */
    ValidationResult validate(MuleEvent event);
}

Below is what the ValidationResult looks like:

/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.extension.validation.api;

/**
 * The result of a validation
 *
 * @see Validator
 * @since 3.7.0
 */
public interface ValidationResult
{

    /**
     * Returns a message associated with the execution
     * of the validation. If the validation failed
     * (which means that {@link #isError()} is {@code true}),
     * then it will contain the reason why the error was generated.
     * Otherwise, it might or might not contain some additional
     * consideration about the validation result
     *
     * @return a {@link String} or {@code null}
     */
    String getMessage();

    /**
     * Whether the validation has failed or not
     *
     * @return {code true} if the validation failed. {@code false} otherwise
     */
    boolean isError();
}

Your implementations of the Validator interface are required to:

  • Have a default constructor

  • Be thread-safe

  • Not have any side effects over the message payload or any of its variables

Once you’ve done that, you can execute the validator through the custom-validator message processor:

<validation:custom-validator class="com.myproject.CustomValidator" config-ref="validator" />

Each instance of custom-validator creates its own instance of the given class and holds it, thus the requirement to be thread-safe. The recommended way of achieving thread safeness is by being stateless, but that’s up to the developer. Another option is to reference the custom validator via a registry name:

<mule>
  <spring:beans>
    <spring:bean id="customValidator" class="com.myproject.CustomValidator" />
  </spring:beans>
  <flow name="customValidator">
    <validation:custom-validator ref="customValidator" />
  </flow>
</mule>

Notice that once more, the instance of the validator to be used is always the same across different executions and thus the validator needs to be thread-safe. Your implementation of Validator can be either within the project or in a jar you add to the classpath. It doesn’t matter as long as it’s instantiable by the application’s class loader.

Customizing Exception Class and Message

There are some cases in which the user might require low level control on how exceptions are created when a validation fails. For that, the following interface is used:

/**
 * A factory for {@link Exception}s which represents
 * a validations which failed. Methods in this class
 * should always be invoked using a {@link ValidationResult}
 * object which {@@link ValidationResult#isError()} method
 * returns {@code true}
 *
 * @since 3.7.0
 */
public interface ExceptionFactory
{

    /**
     * Creates an exception of the given {@code exceptionClass}
     * which represents the given {@code result}.
     * The actual rules about what conditions is {@code exceptionClass}
     * expected to meet (e.g: presence of default constructor)
     * are up to the implementations.
     *
     * @param result         a {@link ValidationResult} which contains information about an error
     * @param exceptionClass the {@link Class} of the exception to be created
     * @param event          the {@link MuleEvent} on which validation failed
     * @param <T>            the type of the exception to be created
     * @return an {@link Exception} if type {@code T}
     */
    <T extends Exception> T createException(ValidationResult result, Class<T> exceptionClass, MuleEvent event);

    /**
     * Creates an exception of the given {@code exceptionClassName}
     * which represents the given {@code result}.
     * <p/>
     * The actual rules about what conditions is the exception {@link Class}
     * expected to meet (e.g: presence of default constructor)
     * are up to the implementations.
     *
     * @param result             a {@link ValidationResult} which contains information about an error
     * @param exceptionClassName the name of the exception {@link Class} to be thrown
     * @param event              the {@link MuleEvent} on which validation failed
     * @return a {@link Exception} of type {@code exceptionClassName}
     */
    Exception createException(ValidationResult result, String exceptionClassName, MuleEvent event);
}

The above interface receives the Event that was rejected by the validation and the validator that raised the error. This method is intended to return the exception to be thrown but not to throw it. Implementations of this interface should never throw exceptions. They should also be thread-safe and have a public default constructor.

See Also