Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.event.spi.PreUpsertEvent;
import org.hibernate.event.spi.PreUpsertEventListener;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.persister.entity.EntityPersister;

Expand All @@ -33,6 +31,7 @@
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;

import static org.hibernate.boot.beanvalidation.BeanValidationLogger.BEAN_VALIDATION_LOGGER;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize;

Expand All @@ -44,10 +43,9 @@
*/
//FIXME review exception model
public class BeanValidationEventListener
implements PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener, PreUpsertEventListener, PreCollectionUpdateEventListener,
SessionFactoryObserver {

private static final CoreMessageLogger LOG = CoreLogging.messageLogger( BeanValidationEventListener.class );
implements SessionFactoryObserver,
PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener, PreUpsertEventListener,
PreCollectionUpdateEventListener {

private final HibernateTraversableResolver traversableResolver;
private final Validator validator;
Expand Down Expand Up @@ -130,7 +128,7 @@ private <T> void validate(
final Set<ConstraintViolation<?>> propagatedViolations = setOfSize( constraintViolations.size() );
final Set<String> classNames = new HashSet<>();
for ( var violation : constraintViolations ) {
LOG.trace( violation );
BEAN_VALIDATION_LOGGER.trace( violation );
propagatedViolations.add( violation );
classNames.add( violation.getLeafBean().getClass().getName() );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package org.hibernate.boot.beanvalidation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;

import org.hibernate.HibernateException;
Expand All @@ -15,19 +14,19 @@
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;

import static org.hibernate.boot.beanvalidation.BeanValidationLogger.BEAN_VALIDATION_LOGGER;

/**
* In {@link Integrator} for Bean Validation.
*
* @author Steve Ebersole
*/
public class BeanValidationIntegrator implements Integrator {

private static final CoreMessageLogger LOG = CoreLogging.messageLogger( BeanValidationIntegrator.class );

public static final String APPLY_CONSTRAINTS = "hibernate.validator.apply_to_ddl";

public static final String JAKARTA_BV_CHECK_CLASS = "jakarta.validation.ConstraintViolation";
Expand Down Expand Up @@ -87,62 +86,35 @@ public void integrate(
BootstrapContext bootstrapContext,
SessionFactoryImplementor sessionFactory) {
final var serviceRegistry = sessionFactory.getServiceRegistry();
final var cfgService = serviceRegistry.requireService( ConfigurationService.class );
// IMPL NOTE: see the comments on ActivationContext.getValidationModes() as to why this is multi-valued...
Object modeSetting = cfgService.getSettings().get( JAKARTA_MODE_PROPERTY );
if ( modeSetting == null ) {
modeSetting = cfgService.getSettings().get( MODE_PROPERTY );
}
final var modes = ValidationMode.getModes( modeSetting );
if ( modes.size() > 1 ) {
LOG.multipleValidationModes( ValidationMode.loggable( modes ) );
}
if ( modes.size() == 1 && modes.contains( ValidationMode.NONE ) ) {
// we have nothing to do; just return
return;
final var modes = getValidationModes( serviceRegistry );
switch ( modes.size() ) {
case 0:
// should never happen, since getValidationModes()
// always returns at least one mode
return;
case 1:
if ( modes.contains( ValidationMode.NONE ) ) {
// we have nothing to do; just return
return;
}
break;
default:
BEAN_VALIDATION_LOGGER.multipleValidationModes( ValidationMode.loggable( modes ) );
}
activate( metadata, sessionFactory, serviceRegistry, modes );
}

private void activate(Metadata metadata, SessionFactoryImplementor sessionFactory, ServiceRegistryImplementor serviceRegistry, Set<ValidationMode> modes) {
final var classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
// see if the Bean Validation API is available on the classpath
if ( isBeanValidationApiAvailable( classLoaderService ) ) {
// and if so, call out to the TypeSafeActivator
try {
final Class<?> typeSafeActivatorClass = loadTypeSafeActivatorClass( classLoaderService );
final Method activateMethod = typeSafeActivatorClass.getMethod( ACTIVATE_METHOD_NAME, ActivationContext.class );
final ActivationContext activationContext = new ActivationContext() {
@Override
public Set<ValidationMode> getValidationModes() {
return modes;
}

@Override
public Metadata getMetadata() {
return metadata;
}

@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}

@Override
public SessionFactoryServiceRegistry getServiceRegistry() {
return (SessionFactoryServiceRegistry) serviceRegistry;
}
};

try {
activateMethod.invoke( null, activationContext );
}
catch (InvocationTargetException e) {
if ( e.getTargetException() instanceof HibernateException exception ) {
throw exception;
}
throw new IntegrationException( "Error activating Bean Validation integration", e.getTargetException() );
}
catch (Exception e) {
throw new IntegrationException( "Error activating Bean Validation integration", e );
}
final var activationContext =
new ActivationContextImpl( modes, metadata, sessionFactory,
(SessionFactoryServiceRegistry) serviceRegistry );
callActivateMethod( classLoaderService, activationContext );
}
catch (NoSuchMethodException e) {
throw new HibernateException( "Unable to locate TypeSafeActivator#activate method", e );
Expand All @@ -155,14 +127,45 @@ public SessionFactoryServiceRegistry getServiceRegistry() {
}
}

private void callActivateMethod(ClassLoaderService classLoaderService, ActivationContext activationContext)
throws NoSuchMethodException {
final var activateMethod =
loadTypeSafeActivatorClass( classLoaderService )
.getMethod( ACTIVATE_METHOD_NAME, ActivationContext.class );
try {
activateMethod.invoke( null, activationContext );
}
catch (InvocationTargetException e) {
final var targetException = e.getTargetException();
throw targetException instanceof HibernateException exception
? exception
: new IntegrationException( "Error activating Bean Validation integration",
targetException );
}
catch (Exception e) {
throw new IntegrationException( "Error activating Bean Validation integration", e );
}
}

private static Set<ValidationMode> getValidationModes(ServiceRegistry serviceRegistry) {
final var settings =
serviceRegistry.requireService( ConfigurationService.class )
.getSettings();
Object modeSetting = settings.get( JAKARTA_MODE_PROPERTY );
if ( modeSetting == null ) {
modeSetting = settings.get( MODE_PROPERTY );
}
return ValidationMode.parseValidationModes( modeSetting );
}

private boolean isBeanValidationApiAvailable(ClassLoaderService classLoaderService) {
try {
classLoaderService.classForName( JAKARTA_BV_CHECK_CLASS );
return true;
}
catch (Exception e) {
return false;
}
return true;
}

/**
Expand Down Expand Up @@ -192,4 +195,32 @@ private Class<?> loadTypeSafeActivatorClass(ClassLoaderService classLoaderServic
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
// nothing to do here afaik
}

private record ActivationContextImpl(
Set<ValidationMode> modes,
Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry)
implements ActivationContext {

@Override
public Set<ValidationMode> getValidationModes() {
return modes;
}

@Override
public Metadata getMetadata() {
return metadata;
}

@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}

@Override
public SessionFactoryServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.beanvalidation;

import org.hibernate.Internal;
import org.hibernate.internal.log.SubSystemLogging;

import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.Cause;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;

import java.lang.invoke.MethodHandles;

import static org.hibernate.cfg.ValidationSettings.JAKARTA_VALIDATION_MODE;
import static org.jboss.logging.Logger.Level.DEBUG;
import static org.jboss.logging.Logger.Level.INFO;
import static org.jboss.logging.Logger.Level.WARN;

/**
* Sub-system logging related to Bean Validation integration
*/
@SubSystemLogging(
name = BeanValidationLogger.NAME,
description = "Logging related to Bean Validation integration"
)
@MessageLogger(projectCode = "HHH")
@ValidIdRange(min = 101001, max = 101500)
@Internal
public interface BeanValidationLogger extends BasicLogger {
String NAME = SubSystemLogging.BASE + ".beanvalidation";

BeanValidationLogger BEAN_VALIDATION_LOGGER = Logger.getMessageLogger( MethodHandles.lookup(), BeanValidationLogger.class, NAME );

@LogMessage(level = DEBUG)
@Message(id = 101001, value = "Unable to acquire Jakarta Validation ValidatorFactory, skipping activation")
void validationFactorySkipped();

@LogMessage(level = DEBUG)
@Message(id = 101002, value = "Skipping application of relational constraints from legacy Hibernate Validator")
void skippingLegacyHVConstraints();

@LogMessage(level = DEBUG)
@Message(id = 101003, value = "ConstraintComposition type could not be determined. Assuming AND")
void constraintCompositionTypeUnknown(@Cause Throwable ex);

@LogMessage(level = DEBUG)
@Message(id = 101004, value = "@NotNull was applied to attribute [%s] which is defined (at least partially) by formula(s); formula portions will be skipped")
void notNullOnFormulaPortion(String propertyName);

@LogMessage(level = WARN)
@Message(id = 101005, value = "Unable to apply constraints on DDL for %s")
void unableToApplyConstraints(String className, @Cause Exception e);

@LogMessage(level = INFO)
@Message(id = 101006, value = "'" + JAKARTA_VALIDATION_MODE + "' named multiple values: %s")
void multipleValidationModes(String modes);

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.CheckConstraint;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
Expand All @@ -52,6 +50,7 @@

import static java.util.Collections.disjoint;
import static org.hibernate.boot.beanvalidation.BeanValidationIntegrator.APPLY_CONSTRAINTS;
import static org.hibernate.boot.beanvalidation.BeanValidationLogger.BEAN_VALIDATION_LOGGER;
import static org.hibernate.boot.beanvalidation.GroupsPerOperation.buildGroupsForOperation;
import static org.hibernate.boot.model.internal.BinderHelper.findPropertyByName;
import static org.hibernate.cfg.ValidationSettings.CHECK_NULLABILITY;
Expand All @@ -68,7 +67,6 @@
*/
class TypeSafeActivator {

private static final CoreMessageLogger LOG = CoreLogging.messageLogger( TypeSafeActivator.class );

/**
* Used to validate a supplied ValidatorFactory instance as being castable to ValidatorFactory.
Expand Down Expand Up @@ -98,10 +96,10 @@ else if ( validationModes.contains( ValidationMode.DDL ) ) {
throw new IntegrationException( "Jakarta Validation provider was not available, but 'ddl' validation mode was requested", exception );
}
else {
if ( exception.getCause() != null && exception.getCause() instanceof NoProviderFoundException ) {
if ( exception.getCause() instanceof NoProviderFoundException ) {
// all good, we are looking at the ValidationMode.AUTO, and there are no providers available.
// Hence, we just don't enable the Jakarta Validation integration:
LOG.debug( "Unable to acquire Jakarta Validation ValidatorFactory, skipping activation" );
BEAN_VALIDATION_LOGGER.validationFactorySkipped();
return;
}
else {
Expand Down Expand Up @@ -172,7 +170,7 @@ private static boolean isConstraintBasedValidationEnabled(ActivationContext cont
|| modes.contains( ValidationMode.AUTO );
}
else {
LOG.debug( "Skipping application of relational constraints from legacy Hibernate Validator" );
BEAN_VALIDATION_LOGGER.skippingLegacyHVConstraints();
return false;
}
}
Expand Down Expand Up @@ -215,7 +213,7 @@ public static void applyRelationalConstraints(
);
}
catch (Exception e) {
LOG.unableToApplyConstraints( className, e );
BEAN_VALIDATION_LOGGER.unableToApplyConstraints( className, e );
}
}
}
Expand Down Expand Up @@ -362,11 +360,11 @@ private static boolean isConstraintCompositionOfTypeOr(
final Method valueMethod = annotation.annotationType().getMethod( "value" );
final Object result = valueMethod.invoke( annotation );
return result != null && "OR".equals( result.toString() );
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
LOG.debug( "ConstraintComposition type could not be determined. Assuming AND", ex );
return false;
}
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
BEAN_VALIDATION_LOGGER.constraintCompositionTypeUnknown( ex );
return false;
}
}
}
return false;
Expand Down Expand Up @@ -429,11 +427,7 @@ private static void markNotNull(Property property) {
column.setNullable( false );
}
else {
LOG.debugf(
"@NotNull was applied to attribute [%s] which is defined (at least partially) " +
"by formula(s); formula portions will be skipped",
property.getName()
);
BEAN_VALIDATION_LOGGER.notNullOnFormulaPortion( property.getName() );
}
}
}
Expand Down
Loading