concept overview

!!!this overview is not finished!!!


hint to understand some concepts: extval introduces no requirement for annotations!


extval release numbers are directly linked with the jsf version.

extval 1.1.x is compatible with jsf 1.1.x
extval 1.2.x is compatible with jsf 1.2.x
extval 2.0.x is compatible with jsf 2.0.x
...

all concepts are available for all jsf versions. to keep it short hints for specific extval versions use just the release number e.g. x.x.3 or just r3

startup

extval introduces so called startup-listener which are simple phase listener. a startup listener is executed before restore view and de-registers itself after the init-method was executed.
so the logic of a startup-listener is called just once at the very beginning of the jsf lifecycle (at the first lifecycle execution of the whole application).

implement a startup-listener:
extend org.apache.myfaces.extensions.validator.core.startup.AbstractStartupListener
implement protected void init()

default convention:
org.apache.myfaces.extensions.validator.custom.StartupListener

use this convention within your webapp. if you provide e.g. a validation module as jar file you can define the startup-listener within the faces-config of the module
(e.g. see: TrinidadModuleStartupListener)

the ExtValStartupListener registers the default ValidationInterceptor of the extval core and executes org.apache.myfaces.extensions.validator.custom.StartupListener (if available)


render-kit factory

this factory type is the starting point of the extval mechanism. it's the result of several concept iterations.

old concepts (not used any more):

new/current concept:

ExtValRenderKitFactory

it decorates the original render-kit factory and delegates everything to the decorated factory.
furthermore, it adds the following behaviour to the getRenderKit method:

AbstractRenderKitWrapperFactory

it creates a wrapper for a given render-kit.
the DefaultRenderKitWrapperFactory decorates the original render-kit with a new instance of ExtValRenderKit

it's possible to add a custom implementation. if a custom implementation is available it gets executed before the default implementation.

adding a custom implementation:
example:

ExtValContext.getContext().getFactoryFinder().getFactory(FactoryNames.RENDERKIT_WRAPPER_FACTORY, AbstractRenderKitWrapperFactory.class).addRenderKitWrapperFactory(new CustomRenderKitWrapperFactory());

it's possible to add multiple factories. the factory which is added at the end is the first factory which is invoked. if a factory returns a result it's used as final result. if it returns null the next factory gets called.

ExtValRenderKit

it extends javax.faces.render.RenderKit
it's the default wrapper implementation and delegates everything to the wrapped render-kit.
furthermore, it's responsible to wrap renderer which are created by the original render-kit with the ExtValRendererWrapper.

ExtValRendererWrapper

it wraps the original renderer. internally it adds a proxy around the wrapped renderer. this proxy should avoid duplicate calls of renderer methods.

we need such a wrapper to allow:

(implemented via renderer interceptors)

ExtValRendererProxy and RendererProxyEntry

it's a proxy for the original renderer which avoids multiple calls of the original renderer method.

q: why is such a proxy essential?
a: each renderer interceptor is able to force the execution of the original renderer method at a specific point. the proxy avoids further calls to the original renderer method. in case of getConvertedValue it's a performance improvement (converted values are cached).

if extval causes a wrong rendering behaviour, try to reset the proxy.

to reset such a proxy use:

ExtValContext.getContext().addGlobalProperty(ExtValRendererProxy.KEY, null)

or you can add a custom proxy (e.g. see: TrinidadModuleStartupListener)

RendererProxyEntry is an internal request scoped data-structure which holds the information which renderer method is already called (one entry per component instance)

renderer interceptor

it's possible to intercept method calls of the original renderer. you can add multiple interceptors.
you can register such an interceptor within a startup listener via:

ExtValContext.getContext().registerRendererInterceptor(new CustomValidationInterceptor());

available methods:

AbstractRendererInterceptor is an internal convenience implementation which implements all methods (except getInterceptorId) with an empty implementation. so it's possible to extend it and just override the method you are interested in.

if beforeGetConvertedValue throws a SkipRendererDelegationException, getReturnValueOnException of SkipRendererDelegationException is called.
getReturnValueOnException calls getReturnValueOnSkipRendererDelegationException of the renderer interceptor which threw the exception. so it's possible to return an alternative result.

(via exceptions it's possible to change the execution of renderer interceptors and the delegation to the original renderer method)

it's also possible to deregister or deny interceptors.
to deregister means:

to deny means:

extval provides two implementations:


ValidationInterceptor

it's the default validation mechanism of the core.

process of implemented mechanisms:

PropertyValidationInterceptor / ValidationInterceptorWithSkipValidationSupport

extends the basic implementation and adds the skip validation feature of the property-validation module.

since extval r3 the name is not longer ValidationInterceptorWithSkipValidationSupport - the new name is: PropertyValidationInterceptor.
it just provides the module key. so it's possible e.g. to register module aware meta-data extraction interceptors. (see org.apache.myfaces.extensions.validator.core.ValidationModuleAware)

the skip validation logic is now provided via a pluggable SkipValidationEvaluator which implements org.apache.myfaces.extensions.validator.core.validation.SkipValidationEvaluator

component initialization

a component initializer is responsible to initialize a component with the information provided by the metadata of a bound value. it's used to join specific mechanisms of component libs.

e.g.:

default convention:
org.apache.myfaces.extensions.validator.custom.ComponentInitializer

via extval java api:

ExtValContext.getContext().addComponentInitializer(new CustomComponentInitializer());

metadata extractor

it's responsible to extract the metadata of a given object.
an instance of PropertyInformation is returned.

available implementations:

PropertyInformation

a data-structure which represents a property bound to an input component.
it's possible to get and set information for the property. such information are e.g. PropertyDetails
it's used internally to add PropertyDetails, however, it's possible to add custom information via the same api.

furthermore, it contains metadata entries. one metadata entry provides the information of a specific annotation.

PropertyDetails

it contains basic information of a property:

it's added during the metadata extraction process via:

propertyInformation.setInformation(PropertyInformationKeys.PROPERTY_DETAILS, propertyDetails);

MetaDataEntry

it contains a key (in case of annotations: package name + annotation name) and a value (any object is possible - in case of annotations it's the annotation of the property).
furthermore, it contains properties. the default implementation sets a reference to the information map to these properties. so it's possible to access these information through a metadata entry. that's not the cleanest approach, however, several concept iterations showed that it's the easiest and cleanest approach to forward these information to the other mechanisms.

it's added during the metadata extraction process via

propertyInformation.addMetaDataEntry(createMetaDataEntryForAnnotation(annotation));

DefaultComponentMetaDataExtractor

it uses the el-helper to extract PropertyDetails of the target property.
furthermore, it adds a MetaDataEntry for each annotation of the property.
the annotations of:

skip validation support

the ValidationInterceptor of the core prepares the skip validation feature. ValidationInterceptorWithSkipValidationSupport implements this feature for the property-validation module. that means that the ValidationInterceptor of the core gets deactivated if the property-validation module is used. within the startup listener of the property-validation module it's done via:

ExtValContext.getContext().denyRendererInterceptor(ValidationInterceptor.class);
//ExtValContext.getContext().registerRendererInterceptor(new ValidationInterceptorWithSkipValidationSupport()); //in versions < r3
ExtValContext.getContext().registerRendererInterceptor(new PropertyValidationInterceptor()); //since r3

since r3 the mechanism is more flexible and it's possible to register a custom implementation of org.apache.myfaces.extensions.validator.core.validation.SkipValidationEvaluator

example for replacing the default evaluator within a startup listener:

ExtValContext.getContext().setSkipValidationEvaluator(new CustomValidationSkipValidationEvaluator());

since extval follows the principle that there's no requirement for annotations the validation strategy has to provide the information. annotate the validation strategy class with @SkipValidationSupport, if it should be possible to skip the validation via @SkipValidation. if a condition of @SkipValidation is true all following skip-able validation strategies (and also metadata transformer) are skipped.

implementation details: @SkipValidation also has a validation strategy. if the condition is true the strategy adds an information to the current metadata entry that the validation of further annotations should be skipped (if supported). that works because all metadata entries for a property share the same information (of the property). so this information is available for all following validations.

MetaDataExtractionInterceptor

(since 1.x.2)

to get a better performance for the jsr 303 integration this type of interceptor was introduced.
e.g. in case of xml configured class based validation it isn't required to have an annotation in place. so such an interceptor has to add the information provided by xml configs to the PropertyInformation object.
the alternative (without changes) would be an additional renderer-interceptor (this would lead to double extraction for each property). furthermore, MetaDataExtractionInterceptors allows the core to be independent of jsr 303 mechanisms. moreover, it also allows similar 3rd party mechanisms.

adding a custom implementation:

ExtValContext.getContext().addMetaDataExtractionInterceptor(new CustomMetaDataExtractionInterceptor());

MetaDataTransformer

it's responsible for unifying the metadata information. e.g. @Column(nullable = false); @Required; Length(minimum = 1);... express that the field is required. so this information is distilled to a generic form. this generic form is used by component-initializers to initialize a component (based on metadata-information).

ProcessedInformationRecorder

it provides the possibility to register recorders which record the converted object of a component. it's the first step of cross-validation (see: CrossValidationUserInputRecorder).

to register a custom recorder use:

ExtValContext.getContext().addProcessedInformationRecorder(new CustomUserInputRecorder());

ValidationStrategy

it's the base interface which is used by the ValidationInterceptor. since we hooked in the conversion we have to throw a ConverterException instead of a ValidatorException.
that wouldn't be nice. so all extval validation strategies extend the AbstractValidationStrategy which translates ValidatorExceptions to ConverterExceptions.

it isn't called validator to avoid confusions. jsf provides validators, bean validation (jsr 303) also provides validators. so it would be confusing to have 3 kinds which are called validator.

furthermore, the AbstractValidationStrategy implements the ValidationException interception mechanism.

to build a custom validation infrastructure it's possible to register a custom RendererInterceptor. so it's possible to reuse the core mechanism and design a custom infrastructure.

since r3 it isn't required to check for empty values. if a validation strategy has to be aware of empty and/or null values, it should be annotated with:

these annotations don't get inherited. so if you would like to change a default implementation to be aware of null you can easily extend the default implementation, annotate the new strategy and change the behavior via the custom implementation.

@NullValueAwareValidationStrategy

if the value to be validated is null a validation strategy only gets invoked with this null value if it is annotated with @NullValueAwareValidationStrategy

@EmptyValueAwareValidationStrategy

if the value to be validated is empty a validation strategy only gets invoked with this empty value if it is annotated with @EmptyValueAwareValidationStrategy

MessageResolver

per default validation strategies reuse the jsf validation messages. anyway, extval provides much more. out-of-the-box several default validation strategies use the message resolver mechanism which uses validationErrorMsgKey of an annotation to resolve the internationalized message for a given key. if there's no value for the key, the equivalent jsf message is used (if possible).

the default implementation uses message bundles. anyway, it's possible to provide an implementation for any data source.

it's possible to provide a message resolver per:

furthermore, for custom validation strategies it's possible to inject a central message resolver of the application via dependency injection.

ValidationExceptionInterceptor

with validation strategies which extend AbstractValidationStrategy or CrossValidationStrategy it's possible to intercept ValidatorExceptions.

default convention:
org.apache.myfaces.extensions.validator.custom.ValidationExceptionInterceptor


ExtValContext

it's the central application scoped context to get/add/remove implementations of different mechanisms.


factories

extval uses several factories. furthermore, it's possible to provide custom implementations. all factories are resolved via the FactoryFinder

ClassMappingFactory

it's the central interface for mapping one class to an other one. the default implementations use the name mapping concept.

name mapping

this concept is used to implement different name conventions. a name of an artifact is mapped to a name of an other artifact. via custom name mappers it's possible to provide custom implementations of name mappers.

StaticConfiguration

it's possible to register static configurations for several mechanisms at the startup. the default implementations of factories cache the mapping. at the first call a factory adds the static configs to the internal cache.


storages

this concept allows to implement storages with different scopes and functionality but a generic mechanism to manage them.
developers can resolve and reset storages via a generic api. to identify a storage a storage-type and storage-name is required. so it's possible to have multiple storages with the same type.

e.g. the GroupStorage is request scoped and the MetaDataStorage is application scoped furthermore both have a different functionality.

resolve a storage:

public <T> T getStorage(Class<T> storageType, String storageName)
{
    return (T)getStorageManagerFactory().create(storageType).create(storageName);
}

reset a storage:

public void resetStorage(Class storageType, String storageName)
{
    getStorageManagerFactory().create(storageType).reset(storageName);
}

resolve the storage manager factory:

private static ClassMappingFactory<Class, StorageManager> getStorageManagerFactory()
{
    return (ExtValContext.getContext().getFactoryFinder()
        .getFactory(FactoryNames.STORAGE_MANAGER_FACTORY, ClassMappingFactory.class));
}

the storage manager factory works via the name-mapper mechanism of extval. it allows to map a storage type to the storage manager.

StorageManager

a storage manager gets registered in the factory and mapped to the type. it is responsible to manage multiple storages of the same type. furthermore, it allows to create and reset a storage with a specific name.
storage manager implementations provide a key which allows to store the storages via an internal key. storage managers can also use name mappers to map from the storage interface to the storage implementation

StorageManagerHolder

allows to get and set storage managers for a given type. it's a factory to create storage managers (factory name: STORAGE_MANAGER_FACTORY)

implementing a new storage

public interface MyStorage
{
    //custom methods - e.g.:
    void add(String key, MyObject1 o1, MyObject2 o2, ...);
    MyObject1 getO1(String key);
    MyObject2 getO2(String key);
}

//there are already implementations for request and application scoped storage implementations
//which extend a generic implementation (AbstractStorageManager) which uses the name mapper concept
public class MyStorageManager extends AbstractRequestScopeAwareStorageManager<MyStorage>
{
    public MyStorageManager()
    {
        register(new MyNameMapper());
    }

    public String getStorageManagerKey()
    {
        return "[unique key for my-storage]";
    }
}

public class MyNameMapper implements NameMapper<String>
{
    public String createName(String source)
    {
        return (MyStorage.class.getName().equals(source)) ? MyStorageImpl.class.getName() : null;
    }
}

//1. arg: storage type 2. arg: storage name
ExtValUtils.getStorage(MyStorage.class, "st1").add(...);
ExtValUtils.getStorage(MyStorage.class, "st2").add(...);

ExtValUtils.getStorage(MyStorage.class, "st1").getO1(...);

ExtValUtils.resetStorage(MyStorage.class, "st1");

get the storage manager holder

StorageManagerHolder storageManagerHolder = (ExtValContext.getContext().getFactoryFinder()
  .getFactory(FactoryNames.STORAGE_MANAGER_FACTORY, StorageManagerHolder.class));

register custom storage managers

storageManagerHolder.setStorageManager(MyStorage.class, new MyStorageManager(), true);


conventions

conventions (e.g. name mapping) are used to allow zero configuration. configuration is just optional and overrules conventions.

list of conventions:

InformationProviderBean

it's the central mechanism to customize conventions.


el-helper

it's a pluggable mechanism to get PropertyDetails, ... of the bound property and other el topics used by extval.

setting a custom el-helper via a startup-listener (a short example)

public class ELHelperStartupListener extends AbstractStartupListener
{
    protected void init()
    {
        ExtValContext.getContext().getFactoryFinder().getFactory(FactoryNames.EL_HELPER_FACTORY, AbstractELHelperFactory.class).setCustomELHelperFactory(new AbstractELHelperFactory() {
            protected ELHelper createELHelper()
            {
                return new DefaultELHelper() {
                    @Override
                    //override methods of the default impl.
                };
            }
        });
    }
}

if you have to provide a completely new impl., you can also use the interface of the el-helper (org.apache.myfaces.extensions.validator.core.el.ELHelper)


jsr 303 integration

(advanced jsr 303 based jsf validation with myfaces extval)

the current goal of this myfaces extval validation-module is to use bean validation (jsr 303) constraints with all jsf versions (1.x and 2.x).

structure

the bean validation (jsr 303) integration is an optional validation module for extval

part of the current structure + the new validation module (validation-modules/bean-validation):

|___component-support
|   |___generic-support
|   |___trinidad-support
|___core
|___validation-modules
    |___bean-validation
    |___property-validation

group validation (partial validation)

jsr 303 specifies group validation. it means that only the constraints which match a given group are validated.
so you can call it "partial validation". details are available here.

jsf 2.0 integrates bean validation via tags
e.g.: <f:validateBean validationGroups="javax.validation.groups.Default,app.validation.groups.Order"/>
first of all it's quite a lot to type the fully qualified class name within a page.
myfaces-core 2 has to impl. this tag, because it's part of the spec.

myfaces extval is able to provide an alternative approach.
extval provides a generic api which looks like:

ExtValBeanValidationContext.getCurrentInstance().addGroup(...)

so it's possible to provide different approaches to define groups which have to be added to this context.
it would be possible via a tag. anyway, to reduce the complexity of pages extval provides annotations for it.

first of all - partial validation shouldn't be used extensively to avoid too complex pages.
the annotaton approach is similar to the view-controller annotation of orchestra.

the idea is to annotate page beans and/or their properties

default group handling

per default every page is validated with the default group (javax.validation.groups.Default). so you don't really need example 1 and 2.
in case of group validation the default group has to be declare explicitly and so example 1 and 2 make sense again to re-add the default group automatically for the given scenarios. that means: normally you don't need to use @BeanValidation explicitly. jsr 303 constraints have the default group (javax.validation.groups.Default) per default :). so the default group (javax.validation.groups.Default) is validated implicitly. if you want to validate 1-n groups and also the default group, you can add the @BeanValidation annotation as class level annotation instead of listing the default group side by side with the other groups.

usage

the following examples are available here
they are based on the hello world application of myfaces.

example 1:

//adds the default group in all cases of group validation
@BeanValidation
public class HelloWorldController {...}

... that means:
the default group (javax.validation.groups.Default) is used for all pages which are using this page bean.

example 2:

public class HelloWorldController
{
    @BeanValidation(viewIds = "/form1.jsp")
    private Person person = new Person();
...
}

... that means:
the default group (javax.validation.groups.Default) is used just for view-id form1.jsp
(if there is also an annotation at the class (see example 1) both information get added to the context.)

example 3:

public class HelloWorldController
{
    @BeanValidation(useGroups = User.class)
    private Person person = new Person();
...
}

... that means:
the User group is used to validate every expression which uses the person property of the hello world controller bean.

example 4:

@BeanValidation(viewIds = "/form1.jsp")
public class HelloWorldController
{
    private Person person = new Person();

    @BeanValidation.List({
            @BeanValidation(viewIds = "/form1.jsp", useGroups = User.class),
            @BeanValidation(viewIds = "/form2.jsp", useGroups = Admin.class)
    })
    public Person getPerson()
    {
        return person;
    }
...
}

... that means:
the default group (javax.validation.groups.Default) as well as the User group is used to validate form1.jsp if the value-binding uses the page-bean
the Admin group is used to validate form2.jsp

possible/logical targets

... that means:
#{pageBean.pageBeanProperty.property1.property2.propertyBase.property}

in this example the >possible< targets are:

summarized:
it's possible to annotate the first bean of an expression, its properties as well as the last base and the final property.
that means: extval scans these logical targets (including super-classes and interfaces) and all levels in between are skipped.

comments

@BeanValidation.List is the jsr 303 style for constraints

conditional validation

example 1:

@BeanValidation(viewIds = "/form1.jsp", useGroups = User.class, conditions = "#{person.role eq 'admin'}")

... that means:
it's only validated if at least one condition returns true.

restricted groups

example 1:

@BeanValidation(viewIds = "/form1.xhtml", useGroups = {User.class, Address.class})
public class HelloWorldController
{
    @BeanValidation(viewIds = "/form1.xhtml", restrictGroups = User.class, conditions = "#{person.role eq 'admin'}")
    private Person person = new Person();
}

... that means:
if this pagebean is used in a value-binding in page form1.xhtml the User and Address group is used for validation. if a(n) in-/direct property of person is the target of the value-binding in page form1.xhtml and the condition returns true the User group isn't used for validation.

model validation

it's possible to activate model validation as well (validation after the "update model values" phase).
if activated, the last base of the bound property is validated.
in case of the example-binding
#{pageBean.pageBeanProperty.property1.property2.propertyBase.property}
the object of propertyBase gets validated per default. all you have to do is to add the @ModelValidation.
optionally you can provide one or more custom target(s) via a conventional el-binding.
per default the validation error messages are displayed via the messages component (e.g. h:messages). if it makes sense, you can display also these model-validation error messages at the component which triggered the model validation process of the object. (to be correct: the message component for the input-component which triggered the model validation.)

in comparison to field based validation, model validation works a bit different.
each @ModelValidation is processed independently whereas information of multiple @BeanValidation for field based validation are merged.

example 1:

@BeanValidation(useGroups = Address.class, modelValidation = @ModelValidation(isActive = true))

... that means:
after the field based bean-validation (jsr 303) validation of all bound properties (in the "process validations" phase), the model is updated by jsf. afterwards model-validation for the address group is executed.

example 2:

@BeanValidation.List({
        @BeanValidation(viewIds = "/form1.jsp", useGroups = Admin.class),
        @BeanValidation(viewIds = "/form1.jsp", useGroups = Address.class,
                modelValidation = @ModelValidation(isActive = true))
})

... that means:
field based validation on page form1.jsp is performed with the admin group and model validation for the same page is performed with the address group.

example 3:

@BeanValidation(useGroups = Address.class, modelValidation = @ModelValidation(isActive = true, displayInline = true))

... that means:
it's like example 1. in contrast to example 1 the validation error messages are displayed inline (at the message component of the component which triggered the model validation)

example 4:

@BeanValidation(useGroups = Address.class,
        modelValidation = @ModelValidation(isActive = true, validationTargets = "#{registrationPage.person.address}"))

... that means:
instead of validating the last base (the propertyBase of the previous binding-example), the address object of the "current" person gets validated.

example 5:

@BeanValidation(useGroups = Address.class,
      modelValidation = @ModelValidation(isActive = true, message = "address is required"))

... that means:
the validation might lead to multiple validation error messages. if it's required to display a single message which summarizes the validation error, it's possible to override the original validation error messages.


drafts

Drafts for new features
Road Map

steps for a release

release steps

Extensions/Validator/DevDoc (last edited 2010-03-24 19:22:16 by 188-22-18-59)