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()