You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by Apache Wiki <wi...@apache.org> on 2009/01/19 14:43:32 UTC

[Myfaces Wiki] Update of "Extensions/Validator/DevDoc" by GerhardPetracek

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Myfaces Wiki" for change notification.

The following page has been changed by GerhardPetracek:
http://wiki.apache.org/myfaces/Extensions/Validator/DevDoc

New page:
= concept overview =

'''!!!this overview is not finished!!!'''

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

== 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.[[BR]]
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:[[BR]]
extend org.apache.myfaces.extensions.validator.core.startup.AbstractStartupListener[[BR]]
implement protected void init()

default convention:[[BR]]
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[[BR]]
(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):
 * manually generated converters ... -> disadvantage: generate converters for all converters used within the application
 * dynamically generated converters ... -> disadvantage: cglib dependency, restoring the proxies due to a special serialization constellation

new/current concept:
 * ExtValRenderKitFactory

=== ExtValRenderKitFactory ===

it decorates the original render-kit factory and delegates everything to the decorated factory.[[BR]]
furthermore, it adds the following behaviour to the getRenderKit method:
 * create a renderkit (delegation to the original factory)
 * get an implementation of AbstractRenderKitWrapperFactory[[BR]](using: ''ExtValContext.getContext().getFactoryFinder().getFactory(FactoryNames.RENDERKIT_WRAPPER_FACTORY, AbstractRenderKitWrapperFactory.class);'')
 * if the wrapper factory is deactivated the created renderkit is returned without a change
 * the wrapper factory creates a wrapper for the renderkit (created at the first step)

=== AbstractRenderKitWrapperFactory ===

it creates a wrapper for a given render-kit.[[BR]]
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:[[BR]]
example:[[BR]]
{{{
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[[BR]]
it's the default wrapper implementation and delegates everything to the wrapped render-kit. [[BR]]
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.[[BR]]

=== 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?[[BR]]
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.[[BR]]

to reset such a proxy use:[[BR]]
{{{
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.[[BR]]
you can register such an interceptor within a startup listener via:[[BR]]
{{{
ExtValContext.getContext().registerRendererInterceptor(new CustomValidationInterceptor());
}}}

available methods:
 * beforeDecode, afterDecode
 * beforeEncodeBegin, afterEncodeBegin
 * beforeEncodeChildren, afterEncodeChildren
 * beforeEncodeEnd, afterEncodeEnd
 * beforeGetConvertedValue, afterGetConvertedValue
 * getInterceptorId
 * getReturnValueOnSkipRendererDelegationException

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.[[BR]]
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.[[BR]]
to deregister means:
 * you know that an interceptor has been registered and you deregister it again.[[BR]]

to deny means:
 * see deregister
 * the interceptor is added to a black list. if the interceptor is registered later on, the registration process is skipped

extval provides two implementations:
 * ValidationInterceptor (basic implementation)
 * ValidationInterceptorWithSkipValidationSupport

----

== ValidationInterceptor ==

it's the default validation mechanism of the core.

process of implemented mechanisms:
 * component initialization (beforeEncodeBegin)
   * extract metadata
   * check skipped validation (if it's the case -> skip initialization)
   * transform metadata
   * invoke all registered instances of ComponentInitializer
 * validation (beforeGetConvertedValue)
   * delegate ''getConvertedValue'' to the decorated renderer
   * invoke all registered instances of ProcessedInformationRecorder (generic mechanism e.g. used for cross-validation)
   * process metadata based validation
     * extract metadata
     * map key of current metadata entry to the validation strategy
     * check skipped validation
     * validate

== ValidationInterceptorWithSkipValidationSupport ==

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

== 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.:
 * some input components are rendered with a visual indication, if they are required (typically it's the: *)
 * join client-side validation of a component lib (see the extval-trinidad-support module)

default convention:[[BR]]
org.apache.myfaces.extensions.validator.custom.ComponentInitializer

via extval java api:[[BR]]
{{{
ExtValContext.getContext().addComponentInitializer(new CustomComponentInitializer());
}}}

== metadata extractor ==

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

available implementations:
 * DefaultComponentMetaDataExtractor
 * DefaultPropertyScanningMetaDataExtractor

=== PropertyInformation ===

a data-structure which represents a property bound to an input component.[[BR]]
it's possible to get and set ''information'' for the property. such information are e.g. PropertyDetails [[BR]]
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:
 * a key which identifies the property (bean + property chain -- it's used for cross-validation)
 * baseObject (the base object of the target property)
 * property (the name of the target property)

it's added during the metadata extraction process via:[[BR]]
{{{
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).[[BR]]
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 [[BR]]
{{{
propertyInformation.addMetaDataEntry(createMetaDataEntryForAnnotation(annotation));
}}}

=== DefaultComponentMetaDataExtractor ===

it uses the el-helper to extract PropertyDetails of the target property.[[BR]]
furthermore, it adds a MetaDataEntry for each annotation of the property.[[BR]]
the annotations of:
 * property
   * get[Property-name]()
   * is[Property-name]()
 * field
   * [property-name]
   * _[property-name]

=== 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());
}}}

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.

== 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.[[BR]]
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.

== 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:
 * validation strategy
 * validation module

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:[[BR]]
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.

----

== conventions ==

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

list of conventions:
 * within the custom base package (also the custom base package is customizable)
   * StartupListener
   * ComponentMetaDataExtractor
   * ComponentInitializer
   * ValidationExceptionInterceptor
   * name mapper
     * ValidationStrategyToMsgResolverNameMapper
     * MetaDataToValidationStrategyNameMapper
     * ValidationStrategyToMetaDataTransformerNameMapper
   * factories
     * MessageResolverFactory
     * ValidationStrategyFactory
     * ComponentMetaDataExtractorFactory
     * MetaDataTransformerFactory
   * resource bundles
     * custom message bundle name
     * custom resource bundle name for static metadata-key -> validation strategy mapping
 * postfix for name mapping
   * ValidationStrategy
   * MetaDataTransformer
   * ValidationErrorMessageResolver

== 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.

----

== JSR 303 integration ==

'''TODO''' (the integration depends on the final version of the specification and the reference implementation)