Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions build/build-config/src/main/resources/setupModules.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def removeDependency(File file, String dependencyToRemove) {
file.write( file.text.replaceAll( /<module name="${dependencyToRemove}"[^\/]*\/>/, '' ) )
}

def appendResourceRoot(File file, String resourcePath) {
file.write( file.text.replaceAll( /<\/resources>/, ' <resource-root path="' + resourcePath + '"/>\n </resources>' ) )
}

// Jakarta Validation API
bvModuleXml = new File( wildflyPatchedTargetDir, 'modules/system/layers/base/jakarta/validation/api/main/module.xml' )
def bvArtifactName = 'jakarta.validation-api-' + project.properties['version.jakarta.validation-api'] + '.jar';
Expand Down Expand Up @@ -48,6 +52,11 @@ appendDependency( hvModuleXml, "javafx.api", true )

deleteFiles( new FileNameByRegexFinder().getFileNames( wildflyPatchedTargetDir + '/modules/system/layers/base/org/hibernate/validator/main', 'hibernate-validator-.*\\.jar' ) )

// Hibernate Accessor
def haArtifactName = 'hibernate-accessor-' + hibernateAccessorVersion + '.jar';
println "[INFO] Using Hibernate Accessor version " + haArtifactName;
appendResourceRoot( hvModuleXml, haArtifactName )

// HV CDI
hvCdiModuleXml = new File( wildflyPatchedTargetDir, 'modules/system/layers/base/org/hibernate/validator/cdi/main/module.xml' )
def hvCdiArtifactName = 'hibernate-validator-cdi-' + project.version + '.jar';
Expand Down
108 changes: 108 additions & 0 deletions documentation/src/main/asciidoc/reference/_ch09.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,114 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter09/CustomScri
----
====

[[section-accessor-factory]]
==== Configuring the `AccessorFactory`

Hibernate Validator uses the `HibernateAccessorFactory` from Hibernate Accessor library to access bean property
values during validation. By default, a reflection-based factory is used which accesses properties via the
standard Java Reflection API.

For improved performance in throughput-sensitive scenarios, Hibernate Validator supports switching to
bytecode-generating accessor factories that produce optimized accessors at startup time, avoiding the overhead
of reflective calls at validation time.
Two such factories are available as separate artifacts:

* *ASM-based*: provided by `org.hibernate.accessor:hibernate-accessor-asm`
* *ByteBuddy-based*: provided by `org.hibernate.accessor:hibernate-accessor-bytebuddy`

To use one of these, add the corresponding dependency to your project:

[source, xml, subs="verbatim,attributes"]
----
<dependency>
<groupId>org.hibernate.accessor</groupId>
<artifactId>hibernate-accessor-bytebuddy</artifactId>
<!-- use the same version as hibernate-accessor -->
</dependency>
----

or

[source, xml, subs="verbatim,attributes"]
----
<dependency>
<groupId>org.hibernate.accessor</groupId>
<artifactId>hibernate-accessor-asm</artifactId>
<!-- use the same version as hibernate-accessor -->
</dependency>
----

===== Programmatic configuration

To configure the accessor factory programmatically, use `HibernateValidatorConfiguration#accessorFactory()`.

[[example-accessor-factory-bytebuddy-programmatically]]
.Configuring the ByteBuddy-based `AccessorFactory`
====
[source, java, indent=0]
----
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.accessorFactory( HibernateAccessorByteBuddyFactory.factory( MethodHandles.lookup() ) )
.buildValidatorFactory();
----
====

Or for the ASM-based factory:

[[example-accessor-factory-asm-programmatically]]
.Configuring the ASM-based `AccessorFactory`
====
[source, java, indent=0]
----
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.accessorFactory( HibernateAccessorAsmFactory.factory( MethodHandles.lookup() ) )
.buildValidatorFactory();
----
====

[NOTE]
====
`MethodHandles.lookup()` must be called from a context that has access to the classes whose
properties will be validated. Typically, this means calling it from within the same module as
your domain objects, or module to which these domain objects are opened to, so that the generated
accessors can access package-private or module-private members.
====

===== XML configuration

To select the accessor factory via XML, set the `hibernate.validator.accessor_factory` property
in `META-INF/validation.xml` to the fully-qualified class name of the factory implementation.
The specified class must have a no-arg constructor.

[[example-accessor-factory-xml]]
.Defining the `AccessorFactory` via XML
====
[source, xml, indent=0]
----
<validation-config
xmlns="https://jakarta.ee/xml/ns/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/validation/configuration
https://jakarta.ee/xml/ns/validation/validation-configuration-3.1.xsd"
version="3.1">

<property name="hibernate.validator.accessor_factory">
com.example.MyCustomAccessorFactory
</property>

</validation-config>
----
====

[NOTE]
====
The built-in bytecode-based factories (`HibernateAccessorAsmFactory`, `HibernateAccessorByteBuddyFactory`)
require a `MethodHandles.Lookup` argument and therefore cannot be used via XML configuration.
Programmatic configuration is the recommended approach for these factories.
====

==== Logging of values under validation

In some cases it might be useful to inspect logs produced by Hibernate Validator.
Expand Down
5 changes: 5 additions & 0 deletions engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.accessor</groupId>
<artifactId>hibernate-accessor</artifactId>
<version>${version.org.hibernate.accessor}</version>
</dependency>

<!--
Provided dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import jakarta.validation.constraints.PastOrPresent;
import jakarta.validation.valueextraction.ValueExtractor;

import org.hibernate.accessor.HibernateAccessorFactory;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.hibernate.validator.constraints.ScriptAssert;
Expand Down Expand Up @@ -122,6 +123,15 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
@Incubating
String GETTER_PROPERTY_SELECTION_STRATEGY_CLASSNAME = "hibernate.validator.getter_property_selection_strategy";

/**
* Property for configuring the accessor factory, allowing to set which {@link HibernateAccessorFactory}
* implementation will be used for accessing bean property values.
*
* @since 9.2.0
*/
@Incubating
String ACCESSOR_FACTORY_CLASSNAME = "hibernate.validator.accessor_factory";

/**
* Property for configuring the property node name provider, allowing to select an implementation of {@link PropertyNodeNameProvider}
* which will be used for property name resolution when creating a property path.
Expand Down Expand Up @@ -399,6 +409,16 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
@Incubating
S getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterPropertySelectionStrategy);

/**
* Allows to set an accessor factory defining the strategy for accessing bean property values.
*
* @param accessorFactory the {@link HibernateAccessorFactory} to be used
* @return {@code this} following the chaining method pattern
* @since 9.2.0
*/
@Incubating
S accessorFactory(HibernateAccessorFactory accessorFactory);

/**
* Allows to set a property node name provider, defining how the name of a property node will be resolved
* when constructing a property path as the one returned by {@link ConstraintViolation#getPropertyPath()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import jakarta.validation.ValidatorFactory;

import org.hibernate.accessor.HibernateAccessorFactory;
import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
Expand Down Expand Up @@ -57,6 +58,16 @@ public interface HibernateValidatorFactory extends ValidatorFactory {
@Incubating
GetterPropertySelectionStrategy getGetterPropertySelectionStrategy();

/**
* Returns the accessor factory used for accessing bean property values.
*
* @return the accessor factory of the current {@link ValidatorFactory}
*
* @since 9.2.0
*/
@Incubating
HibernateAccessorFactory getAccessorFactory();

/**
* Returns the property node name provider used to resolve the name of a property node when creating the property path.
*
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import jakarta.validation.spi.ValidationProvider;
import jakarta.validation.valueextraction.ValueExtractor;

import org.hibernate.accessor.HibernateAccessorFactory;
import org.hibernate.validator.BaseHibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.constraintvalidation.spi.DefaultConstraintValidatorFactory;
Expand Down Expand Up @@ -126,6 +127,7 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
private Duration temporalValidationTolerance;
private Object constraintValidatorPayload;
private GetterPropertySelectionStrategy getterPropertySelectionStrategy;
private HibernateAccessorFactory accessorFactory;
private Set<Locale> locales = Collections.emptySet();
private Locale defaultLocale = Locale.getDefault();
private LocaleResolver localeResolver;
Expand Down Expand Up @@ -380,6 +382,14 @@ public T getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterP
return thisAsT();
}

@Override
public T accessorFactory(HibernateAccessorFactory accessorFactory) {
Contracts.assertNotNull( accessorFactory, MESSAGES.parameterMustNotBeNull( "accessorFactory" ) );

this.accessorFactory = accessorFactory;
return thisAsT();
}

@Override
public T locales(Set<Locale> locales) {
Contracts.assertNotNull( defaultLocale, MESSAGES.parameterMustNotBeNull( "locales" ) );
Expand Down Expand Up @@ -408,7 +418,8 @@ public MethodValidationConfiguration getMethodValidationConfiguration() {
public final DefaultConstraintMapping createConstraintMapping() {
return new DefaultConstraintMapping( new JavaBeanHelper(
getterPropertySelectionStrategy == null ? new DefaultGetterPropertySelectionStrategy() : getterPropertySelectionStrategy,
validationBootstrapParameters.getPropertyNodeNameProvider() == null ? defaultPropertyNodeNameProvider : validationBootstrapParameters.getPropertyNodeNameProvider()
validationBootstrapParameters.getPropertyNodeNameProvider() == null ? defaultPropertyNodeNameProvider : validationBootstrapParameters.getPropertyNodeNameProvider(),
accessorFactory == null ? HibernateAccessorFactory.reflection() : accessorFactory
) );
}

Expand Down Expand Up @@ -576,6 +587,10 @@ public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() {
return getterPropertySelectionStrategy;
}

public HibernateAccessorFactory getAccessorFactory() {
return accessorFactory;
}

@Override
public Set<ValueExtractor<?>> getValueExtractors() {
return validationBootstrapParameters.getValueExtractorDescriptors()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
package org.hibernate.validator.internal.engine;

import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAccessorFactory;
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues;
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint;
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints;
Expand Down Expand Up @@ -42,6 +43,7 @@
import jakarta.validation.ValidatorFactory;
import jakarta.validation.spi.ConfigurationState;

import org.hibernate.accessor.HibernateAccessorFactory;
import org.hibernate.validator.HibernateValidatorContext;
import org.hibernate.validator.HibernateValidatorFactory;
import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory;
Expand Down Expand Up @@ -99,6 +101,8 @@ public class PredefinedScopeValidatorFactoryImpl implements PredefinedScopeHiber

private final GetterPropertySelectionStrategy getterPropertySelectionStrategy;

private final HibernateAccessorFactory accessorFactory;

private final PropertyNodeNameProvider propertyNodeNameProvider;

private final ValidationOrderGenerator validationOrderGenerator;
Expand Down Expand Up @@ -154,6 +158,7 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
this.validationOrderGenerator = new ValidationOrderGenerator();

this.getterPropertySelectionStrategy = ValidatorFactoryConfigurationHelper.determineGetterPropertySelectionStrategy( hibernateSpecificConfig, properties, externalClassLoader );
this.accessorFactory = determineAccessorFactory( hibernateSpecificConfig, properties, externalClassLoader );
this.propertyNodeNameProvider = ValidatorFactoryConfigurationHelper.determinePropertyNodeNameProvider( hibernateSpecificConfig, properties, externalClassLoader );

this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() );
Expand All @@ -169,7 +174,7 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
);

ExecutableHelper executableHelper = new ExecutableHelper( typeResolutionHelper );
JavaBeanHelper javaBeanHelper = new JavaBeanHelper( getterPropertySelectionStrategy, propertyNodeNameProvider );
JavaBeanHelper javaBeanHelper = new JavaBeanHelper( getterPropertySelectionStrategy, propertyNodeNameProvider, accessorFactory );

// first we want to register any validators coming from a service loader. Since they are just loaded and there's
// no control over them (include/exclude the ones that already exists from any other sources etc.)
Expand Down Expand Up @@ -303,6 +308,11 @@ public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() {
return getterPropertySelectionStrategy;
}

@Override
public HibernateAccessorFactory getAccessorFactory() {
return accessorFactory;
}

@Override
public PropertyNodeNameProvider getPropertyNodeNameProvider() {
return propertyNodeNameProvider;
Expand Down
Loading