diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java index 6f327516f0..94f48e4cb0 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.messageinterpolation; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Locale; import javax.el.ExpressionFactory; @@ -13,6 +15,8 @@ import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; +import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; /** @@ -70,13 +74,45 @@ public String interpolate(Context context, Locale locale, String term) { return expression.interpolate( context ); } + /** + * The javax.el FactoryFinder uses the TCCL to load the {@link ExpressionFactory} implementation so we need to be + * extra careful when initializing it. + * + * @return the {@link ExpressionFactory} + */ private static ExpressionFactory buildExpressionFactory() { + // First, we try to load the instance from the TCCL. try { return ExpressionFactory.newInstance(); } + catch (Throwable e) { + // we ignore the error in this case as we will try the Hibernate Validator class loader. + } + + // Then we try the Hibernate Validator class loader. In a fully-functional modular environment such as + // WildFly or Jigsaw, it is the way to go. + final ClassLoader originalContextClassLoader = run( GetClassLoader.fromContext() ); + + try { + run( SetContextClassLoader.action( ResourceBundleMessageInterpolator.class.getClassLoader() ) ); + return ExpressionFactory.newInstance(); + } catch (Throwable e) { // HV-793 - We fail eagerly in case we have no EL dependencies on the classpath throw LOG.getUnableToInitializeELExpressionFactoryException( e ); } + finally { + run( SetContextClassLoader.action( originalContextClassLoader ) ); + } + } + + /** + * Runs the given privileged action, using a privileged block if required. + *

+ * NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary + * privileged actions within HV's protection domain. + */ + private static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } } diff --git a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java index db44ac2521..930c69728d 100644 --- a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java +++ b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java @@ -108,6 +108,8 @@ public static void setLocaleToEnglish() { @Test public void canObtainValidatorFactoryAndPerformValidation() { + Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); + Set> constraintViolations = Validation.byDefaultProvider() .providerResolver( new MyValidationProviderResolver() ) .configure() @@ -116,11 +118,13 @@ public void canObtainValidatorFactoryAndPerformValidation() { .validate( new Customer() ); assertEquals( 1, constraintViolations.size() ); - assertEquals( "must be greater than or equal to 1", constraintViolations.iterator().next().getMessage() ); + assertEquals( "must be greater than or equal to 2", constraintViolations.iterator().next().getMessage() ); } @Test public void canConfigureCustomConstraintValidatorFactoryViaValidationXml() { + Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); + ExampleConstraintValidatorFactory.invocationCounter.set( 0 ); HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ) @@ -146,6 +150,8 @@ public void canConfigureCustomConstraintValidatorFactoryViaValidationXml() { @Test public void canConfigureConstraintViaXmlMapping() { + Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); + Set> constraintViolations = Validation.byProvider( HibernateValidator.class ) .providerResolver( new MyValidationProviderResolver() ) .configure() @@ -160,6 +166,8 @@ public void canConfigureConstraintViaXmlMapping() { @Test public void canConfigureCustomConstraintViaXmlMapping() { + Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); + Set> constraintViolations = Validation.byProvider( HibernateValidator.class ) .providerResolver( new MyValidationProviderResolver() ) .configure() @@ -174,6 +182,8 @@ public void canConfigureCustomConstraintViaXmlMapping() { @Test public void canObtainValuesFromValidationMessages() { + Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); + Set> constraintViolations = Validation.byProvider( HibernateValidator.class ) .providerResolver( new MyValidationProviderResolver() ) .configure()