Sling Validation

Many Sling projects want to be able to validate both Resources and request parameters. Through the Sling Validation framework this is possible with the help of validation model resources which define validation rules for a certain resourceType.

Prerequisites

To use this validation framework the following bundles need to be deployed

  1. org.apache.sling.validation.api
  2. org.apache.sling.validation.core

In addition a service resolver mapping needs to be configured for the service name org.apache.sling.validation.core. The bound service user needs to have at least read access to all resources within the resource resolver's search paths (usually /apps and /libs).

Basic Usage

To validate a resource one first needs to get a ValidationModel and then validate the resource with that model. Both functionalities are provided by the ValidationService OSGi service:

try {
    ValidationModel validationModel = validationService.getValidationModel(resource, true);
    if (validationModel != null) {
  	    ValidationResult result = validationService.validate(resource, validationModel);
  	    if (!result.isValid()) {
    	    // give out validation messages from result.get
  	    }
    }
} catch (IllegalStateException e) {
    // give out error message that the validation model is invalid!
}

Apart from that it is also possible to validate resources including all child resources having their own validation model (i.e. a merged view of the validation models is applied). The appropriate validation model is being looked up by getting the resource type for each node. Since by default the JCR will return the primary type in case there is no sling:resourceType property found on the node, either the 2nd parameter enforceValidation should be set to false or some resource types must be explicitly ignored by the given filter in the 3rd parameter filter to also properly support validation models which have children resources on their own.

try {
    final Predicate ignoreResourceType1Predicate = new Predicate<Resource>() {
        @Override
        public boolean test(final Resource resource) {
            return !"resourcetype1".equals(resource.getResourceType());
        }
    };
  	ValidationResult result = validationService.validateResourceRecursively(resource, false, ignoreResourceType1Predicate, false);
  	if (!result.isValid()) {
    	// give out validation messages from result.getFailureMessages()
  	}
    
} catch (IllegalStateException e) {
    // give out error message that an invalid validation model for at least one sub resource was found
} catch (IllegalArgumentException e) {
    // one of the resource types is absolute or there was no validation model found for at least one sub resource
}

All methods to retrieve a validation model support a boolean parameter considerResourceSuperTypeModels. If this is set to true, the validation model is not only being looked up for exactly the given resource type but also for all its resource super types. The returned model is then a merged model of all found validation model along the resource type hierarchy.

ValidationResult

The ValidationResult indicates whether a given Resource or ValueMap is valid or invalid according to a given validation model. In the latter case it aggregates one or more ValidationFailures. Each ValidationFailure is encapsulating an error message and a severity. The severity may be set on the following locations (where locations on top may overwrite severity from locations below):

  1. validation model (per use case of a Validator)
  2. severity defined on the Validator
  3. the default severity (may be set through the OSGi configuration for PID org.apache.sling.validation.impl.ValidationServiceImpl, is 0 by default)

You have to use a ResourceBundle (Internationalization Support) to resolve the message for a specific locale. By default Sling Validation comes only with English failure messages.

Validation Model Resources

The ValidationModel is constructed from resources with the resourceType sling/validation/model. Those resources are considered validation model resources if they are located below the Sling ResourceResolver search paths (/apps and /libs).

The resources should have the following format:

Property/Resource Name Property or Resource Type Description Mandatory Example Value
sling:resourceType Property String Always sling/validation/model, otherwise model will never be picked up by Sling Validation. yes sling/validation/model
validatingResourceType Property String The resource type of the resource for which this validation model should be applied. Must always be relative to the resource resolver's search path (i.e. not start with a "/"). yes my/own/resourcetype
applicablePaths Property String[] Path prefixes which restrict the validation model to resources which are below one of the given prefixes. No wildcards are supported. If not given, there is no path restriction. If there are multiple validation models registered for the same resource type the one with the longest matching applicablePath is chosen. no /content/mysite
properties\<propertyName> Resource n/a This resource ensures that the property with the name <propertyName> is there. The resource name has no longer a meaning if the property nameRegex is set on this node. no n/a
properties\<propertyName>\optional Property Boolean If true it is not an error if there is no property with the given <propertyName> or none matching the nameRegex. If not set or false the property must be there. no false
properties\<propertyName>\propertyMultiple Property Boolean If true only multivalue properties are allowed with the name <propertyName> or matching the nameRegex. If not set or false, multi- and single-value properties are accepted. no false
properties\<propertyName>\nameRegex Property String If set the <propertyName> has no longer a meaning. Rather all properties which match the given regular expression are considered. At least one match is required, otherwise the validated resource/valuemap is considered invalid. no property[0-8]
properties\<propertyName>\validators\<validatorId> Resource n/a The <validatorId> must be the id of a validator. The id is given by the OSGi service property validator.id set in the validator. Each validators node might have arbitrarily many child resources (one per validator). no n/a
properties\<propertyName>\validators\<validatorId>\validatorArguments Property String[] The parametrization for the validator with the id <validatorId>. Each value must have the pattern key=value. The parametrization differs per validator. no regex=^[a-z]*$
properties\<propertyName>\validators\<validatorId>\severity Property Integer The severity which should be set on all emitted validation failures by this validator. no 0
children\<resourceName> Resource n/a This resource ensures that the resource with the name <resourceName> is there. The resource name has no longer a meaning if the property nameRegex is set on this node. no n/a
children\<resourceName>\nameRegex Property String If set the <resourceName> has no longer a meaning. Rather all resources whose name match the given regular expression are considered. At least one match is required, otherwise the validated resource/valuemap is considered invalid. no child[1-9]
children\<resourceName>\optional Property Boolean If true it is not an error if there is no resource with the given <resourceName> or none matching the nameRegex. If not set or false the resource must be there. no false
children\<resourceName>\properties Resource n/a The properties can be configured on the child level in the same way as on the root level. no n/a

Validation Model Inheritance

Sling Validation optionally supports the inheritance of Sling Validation Models. This means not only the model for exactly the given resource type is considered, but also the models for all resource super types. To overwrite some property or child from one of the super type models, just define a property/child on the same level and with the same name in a model for a resource type which is more specific. That way the property/child on the super validation model is no longer effective.

Precedence of Validation Models

In case there are multiple validation models registered for the same resource type the one gets chosen which has the longest matching applicablePath. In case even that does not resolve to a single model the one in the first resource resolver's search path is chosen (models below /apps before the ones below /libs). If even that does not resolve to a single validation model any of the equally ranked models might be picked.

Usage in Sling Models

Since Sling Models 1.2.0

See Sling Models validation

Before Sling Models 1.2.0

One needs to call the validate method within the PostConstruct method of the according Sling Model

@SlingObject
protected Resource resource;

@OSGiService
protected ValidationService validation;

@PostConstruct
public void validate() {
	try {
    ValidationModel validationModel = validation.getValidationModel(resource);
    if (validationModel == null) {
        LOG.warn("No validation defined for resource '{}' with type '{}'", resource.getPath(), resource.getResourceType());
   
    } else {
        ValidationResult result = validation.validate(resource, validationModel);
        if (!result.isValid()) {
            // give out the validation result
        }
    }
    } catch (IllegalStateException e) {
    	 LOG.warn("Invalid validation model for resource '{}' with type '{}'", resource.getPath(), resource.getResourceType());
    }
}

Validators

Validator ID Description Parameters Since
org.apache.sling.validation.core.RegexValidator Validates that a property value matches a given regular expression regex, mandatory parameter giving a regular expression according to the pattern described in java.util.regex.Pattern. Only if the property value matches this expression it is considered valid. 1.0.0

Writing Validators

To write a validator one needs to implement the org.apache.sling.validation.spi.Validator interface in an OSGi service (look at org.apache.sling.validation.core.RegexValidator for an example). That interface defines the method validate. That is called for each property which is bound to the validator through the validation model. Each validator needs to specify one type parameter which defines upon which classes the validator can act (usually String). Array types are also supported here. Collection types are not supported. If a property value cannot be converted to the requested type from the validator (through ValueMap.get(name, type)), validation will fail.

In addition the OSGi service must expose a String property named validation.id. The value of this property should always start with the providing bundle's symbolic name. Only through this value the validator can be referenced from validation models. If multiple validators have the same validation.id value the one with the highest service ranking gets always chosen.

A validator may also expose a service property named validation.severity with an Integer value. This defines the default severity of the Validator (which may be overwritten in the validation model).

References

  1. Apache Sling Generic Validation Framework, adaptTo 2014
- ( Sling Validation )