diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java index 0eb291ce4293..04bf58a0e805 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java @@ -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; @@ -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; @@ -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; @@ -130,7 +128,7 @@ private void validate( final Set> propagatedViolations = setOfSize( constraintViolations.size() ); final Set classNames = new HashSet<>(); for ( var violation : constraintViolations ) { - LOG.trace( violation ); + BEAN_VALIDATION_LOGGER.trace( violation ); propagatedViolations.add( violation ); classNames.add( violation.getLeafBean().getClass().getName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationIntegrator.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationIntegrator.java index da6dcb0f993a..6152bd463017 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationIntegrator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationIntegrator.java @@ -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; @@ -15,10 +14,12 @@ 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. * @@ -26,8 +27,6 @@ */ 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"; @@ -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 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 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 ); @@ -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 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; } /** @@ -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 modes, + Metadata metadata, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) + implements ActivationContext { + + @Override + public Set getValidationModes() { + return modes; + } + + @Override + public Metadata getMetadata() { + return metadata; + } + + @Override + public SessionFactoryImplementor getSessionFactory() { + return sessionFactory; + } + + @Override + public SessionFactoryServiceRegistry getServiceRegistry() { + return serviceRegistry; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationLogger.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationLogger.java new file mode 100644 index 000000000000..aa3ac5b03886 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationLogger.java @@ -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); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java index 8c35cd8789ea..674181d97bee 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java @@ -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; @@ -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; @@ -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. @@ -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 { @@ -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; } } @@ -215,7 +213,7 @@ public static void applyRelationalConstraints( ); } catch (Exception e) { - LOG.unableToApplyConstraints( className, e ); + BEAN_VALIDATION_LOGGER.unableToApplyConstraints( className, e ); } } } @@ -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; @@ -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() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/ValidationMode.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/ValidationMode.java index 13c774249483..abb88695fac4 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/ValidationMode.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/ValidationMode.java @@ -33,14 +33,19 @@ private String externalForm() { }; } + @Deprecated(since = "7.2", forRemoval = true) public static Set getModes(Object modeProperty) { - final Set modes = setOfSize( 3); + return parseValidationModes( modeProperty ); + } + + public static Set parseValidationModes(Object modeProperty) { + final Set modes = setOfSize( 3 ); if ( modeProperty == null ) { modes.add( ValidationMode.AUTO ); } else { for ( String modeInString : split( ",", modeProperty.toString() ) ) { - modes.add( getMode(modeInString) ); + modes.add( parseValidationMode( modeInString ) ); } } if ( modes.size() > 1 @@ -50,8 +55,8 @@ public static Set getModes(Object modeProperty) { return modes; } - private static ValidationMode getMode(String modeProperty) { - if ( modeProperty == null || modeProperty.isEmpty() ) { + private static ValidationMode parseValidationMode(String modeProperty) { + if ( modeProperty == null || modeProperty.isBlank() ) { return AUTO; } else { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 92cffbe91a45..510ddad591a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -32,7 +32,6 @@ import jakarta.transaction.Synchronization; import org.checkerframework.checker.nullness.qual.Nullable; -import static org.hibernate.cfg.ValidationSettings.JAKARTA_VALIDATION_MODE; import static org.jboss.logging.Logger.Level.DEBUG; import static org.jboss.logging.Logger.Level.ERROR; import static org.jboss.logging.Logger.Level.INFO; @@ -499,10 +498,6 @@ void cannotResolveNonNullableTransientDependencies( ) void explicitSkipLockedLockCombo(); - @LogMessage(level = INFO) - @Message(value = "'" + JAKARTA_VALIDATION_MODE + "' named multiple values: %s", id = 448) - void multipleValidationModes(String modes); - @LogMessage(level = WARN) @Message( id = 449,