Introducing the Validations Module

Facebooktwittergoogle_plusredditpinterestlinkedinmail

This all began with a very popular request: “We want to be able to throw an Exception from a flow”. The motivation for this is that it’s fairly common to run into “business errors” (errors not related to the handling and transmission of data but the data itself) which should actually be treated in the same way as a connection or system error (things like the remote endpoint is down).

Given the popularity of the request we decided to look into it and started by asking: “which are the use cases in which you would throw an exception?”. 100% of the answers were: “because the data was not valid after applying some X criteria”. So, instead of doing a very raw <throw-exception /> message processor we decided to build a validations module, which throws exceptions when a validation fails.

Do we really need this?

It’s a very common use case when doing integrations to need a mechanism to test some conditions are met and throw an exception if validation failed. Mule currently provides some mechanisms that can be used for validations but none of them completely fulfils the use case:

Filters

Filters can stop execution of a flow if a condition is not met, however they’re not a good fit for this because:

  • Filters are conceptually conceived to just kill event’s processing. They can throw exceptions, but you cannot customise the exception type. So if you have two filters in the same flow you cannot know which one failed. They don’t throw an exception or provide any mechanism to take action on that error.
  • Filters often require you to write a MEL expression for each validation. Not an out of the box solution.
  • At the end of the day, reality if that even though filters can sometimes be used as validators, they’re not conceived for that.

Choice + scripting component

Another option is to use a choice element to evaluate the condition and then throw an exception using groovy or other expression evaluator. This solution is quite verbose and overly complex.

  • You have to evaluate the condition through a custom expression
  • You need custom code to actually throw the exception
  • The choice element forces you to an unnecessary <otherwise> clause

Availability

This feature is available starting with Mule ESB 3.7.0, in both Community and Enterprise editions. However, Studio support only became available starting with the September 2015 release.

Validation Basics

The validations module was designed under the following principles:

If validation fails, a ValidationException will be thrown.

  • By default, this exception will have a meaningful message attached. You can optionally customize this message and change the type of exception thrown, as long as the new exception type has a constructor that overrides Exception(String).
  • For cases in which you want to throw an Exception without such a constructor, or which creation is not trivial, or which is to contain additional information, there will be a factory interface you can implement to create that exception

Ways of accessing a validator

In most cases, there’re two ways of using a validator: through a MessageProcessor or through a MEL function.

Through a MessageProcessor

We provide a message processor for each of the validators. This is what you use when you want to perform the validations as part of your flow and you want an exception to be thrown if a validation fails.

If you like to build your flows using the visual designer in Anypoint Studio, then the validators are exposed as a single item in the components palette:

Validators

You can then select the validation type:

Screen Shot 2015-08-28 at 3.26.55 PM

The parameters of the validator you selected are then shown:

Validation screen shot 3

If you prefer to get your hands dirty and write the XML manually, then each validator is a different message processor in the validation namespace:

A nice thing about the message processors, is that they all accept expressions in all their parameters.

Through MEL

The purpose of exposing validators through MEL is to use them in decision making. Unlike the message processor version, these will be boolean functions without an output message, just true if the validation succeeded, or false otherwise. All the validators are available through a validator variable automatically added into the MEL context.

Validate Email address

Checks that a given email is valid.

From XML:

It can also be used through MEL:

Validate using a regular expression

Validates that a given expression matches a Java regular expression.

The caseSensitive parameter is optional and defaults to true.

It can also be used through MEL:

Validate a string is a valid time according to a pattern

Pattern and locale are optional arguments.

  • Locale defaults to the system’s locale
  • Pattern defaults to the locale’s default pattern.

This same validator can also be used to process a timeless date:

Also available through MEL in various overloaded forms:

Validate String, Collection or Map is not empty

In the case of a String, the definition of not empty is that length is greater than zero and it’s not all whitespace characters. In the case of a Collection or Map, it refers to how many items are contained.

Also available through MEL:

Validate String, Collection or Map is empty

The exact opposite of the not empty validator

Validate size

Validates that the input’s size is between a min and max boundaries. This is valid for inputs of type String, Collection, Map and arrays (in the case of the String, the size is actually its length).

  • min is optional and defaults to zero, which in practice means that a blank String is accepted. This number must be in the integer range
  • max is also optional and defaults to null, which in practice means that no upper bound is enforced. This number must be in the integer range

Also available through MEL:

Validate not null

Fails if value is null or an instance of NullPayload

Also available through MEL:

Validate null

Fails if value is not null and not an instance of NullPayload

Also available through MEL:

Validate a String can be transformed to a number

The processors above validates that a String can be parsed as a number of a certain type. minValue and maxValue are optional and allow to check that, if valid, the parsed number is between certain inclusive boundaries. If not provided, then those bounds are not applied.

The valid options for the numberType attribute are:

  • INTEGER
  • LONG
  • DOUBLE
  • SHORT
  • FLOAT

It is also possible to specify a pattern and a locale to perform the validation.

  • Locale defaults to the system locale.
  • Pattern defaults to the locale’s default pattern.

Full form of this validator would be:

Also available through MEL:

Validate IP address

Checks that a given ip address is valid. It supports both IPV4 and IPV6. In the case of IPV6, both full and collapsed addresses are supported, but addresses containing ports are not.

Also available through MEL: #[validator.validateIp(‘127.0.0.1’)]

Validate url

Validates that a given String can be interpreted as an url. This is done by invoking the URL(String) constructor in the java.net.URL class. If that constructor throws exception then the validation fails. Whatever String which that constructor accepts is considered valid.

Also available through MEL with two overloads:

Boolean / Fallback validations

Although the validations above are quite general and cover many use cases, there’s always the possibility that it doesn’t quite matches your use case, that’s why there’re two fallback expressions which simply evaluate that a given expression is true or false:

Notice that:

  • Because conceptually speaking a validator should not modify the message payload or any of its properties, the MEL expression used here is expected to not have any side effects.
  • It makes no sense to expose these through MEL since boolean comparison is something already built into the language.

Custom Validators

If you want to perform complex validation logic, or reuse some code you already have on a separata jar, then the is-true and is-false fallbacks might not be that convenient. You might want to build your own validator.

Leveraging the Validator API, users can create their own validators, and there will be two mechanisms to execute them in a flow. Next we’ll list the 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:

And in case you’re wondering, here’s how the ValidationResult looks like:

Your implementations of the Validator interface are required to:

  • Have a default constructor
  • Be thread-safe
  • Should 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:

Each instance of custom-validator will create its own instance of the given class and will hold 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:

Notice that once more, the instance of the validator to be used will always be the same across different executions and thus the validator needs to be thread-safe. Your implementation of Validator can be either in 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’re 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 will be used:

This interface receives the Event which failed 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 and be thread-safe and have a public default constructor.

Global Level

At a global level, you can only override the default ExceptionFactory. It doesn’t make sense to try to customize a message at this point since you don’t know which validator might fail. You can configure it like this:

You could also provide a reference to a Spring Bean instead:

Notice that you either provide a classname or a bean reference. You can’t do both things.

In Studio, you can do the same by creating a validation:config global element. You can do that by dropping a validation component in your flow and clicking on the add configuration icon:

Screen Shot 2015-08-31 at 10.56.58 AM

Then select the validation configuration:

Screen Shot 2015-08-31 at 11.06.22 AM

You can either provide the classname of an ExceptionFactory or a reference to a Spring Bean.

Screen Shot 2015-08-31 at 11.09.36 AM

Validator Level

On any of these validators you can customize the type of exception thrown by providing the canonical name of an exception type. If that exception type does not override the constructor Exception(String) an IllegalArgumentException will be thrown. You also get the chance to customize the message of the exception thrown.

The above setting overrides the global ExceptionFactory configured in the validation config and creates the exception by those parameter. NotAnAdultException is expected to have a constructor taking one String argument, otherwise it will fail (that will be validated at start time).

NOTE: You don’t have to customize both the exception type and the message, you can also just customize one of them.

In Studio, you can do this clicking on the customize tab that you get on every validator component:

Screen Shot 2015-08-31 at 11.43.14 AM

I18N

Since validators provide a message upon failure, another common concern is how to apply I18N. By default, the common validators provide their message in American English. Those message are not hardcoded but in a resource file. Users wanting to provide their own internationalized messages can do so by specifying their own resources file at a config level:

The i18n is optional, but if you specify it then the bundle Path attribute is mandatory. The locale attribute is optional and defaults to the system locale. However, it is most useful when used with an expression which returns the locale to be applied on the given event:

The example above assumes that by the time the validator is executed, there will be a flowVar called tenantLocale in which the locale to be used has been set (local is optional, it will default to the current locale).

You can also do this in Studio using the global elements config dialog, just like with the ExceptionFactory:

Screen Shot 2015-08-31 at 12.11.53 PM

Validating many conditions at once

There’re scenarios in which you want to evaluate many conditions with the possibility that many of them fail. In those cases, it’s useful to generate only one error with all of the descriptions. This use case is also supported:

About the all validator:

  • All validations are executed, even if all of them fail
  • If any of the validations above fail, one single exception will be thrown. The exception contains a multiline message in which each line corresponds to each failing validation.
  • Validators are executed sequentially using the flow’s thread
  • Since validators don’t have side effects, the order shouldn’t matter
  • Unlike the other validators, the all validator does not allow directly customizing the exception type or the message through a validation:exception or exception factory elements (you can however customize the message of the inner validators though).

In Studio, you can use this by dropping a validation component into your flow and choosing the “All” validator. You’ll get a table below in which you can add/edit/remove your custom validators:

Screen Shot 2015-08-31 at 12.54.09 PM

Example: Validate json Http Post

Let’s wrap this up with a simple example. Suppose that someone is posting the following json through a http listener:

Now consider the following config:

In the example above we validate that:

  • First and last name are not empty strings
  • That the age is a valid integer number above 18
  • That the email address is valid
  • That the social security number has the correct size and matches a regular expression

Valid feature?

That’s it. We hope you like this feature. Have feedback? Please comment!

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Mariano Gonzalez

I first started working on the IT industry in 2001 and I've been working as software architect and team leader since 2006, mainly on BPM/ERP applications for industries in the agriculture, energy, government, IT, telecom and content management industries, serving roles of Team Leader and software architect. Since 2011, I also started to specialize in SaaS applications and integration on the Cloud. I have a strong orientation to both, software architecturing and design as for team management, I'm constantly looking for challenges that allows me to develop and increase my social and technical skills

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>