diff --git a/engine/pom.xml b/engine/pom.xml index 5ec99186ee..5a55ea198f 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -74,12 +74,6 @@ provided true - - org.glassfish.expressly - expressly - provided - true - org.jboss.logging jboss-logging-annotations @@ -125,6 +119,11 @@ + + org.glassfish.expressly + expressly + test + org.testng testng 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 6a81a885c2..dc6c9aac30 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java @@ -5,6 +5,8 @@ package org.hibernate.validator.messageinterpolation; import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.lang.reflect.Method; import java.util.Collections; import java.util.Locale; import java.util.Set; @@ -22,8 +24,6 @@ import org.hibernate.validator.spi.messageinterpolation.LocaleResolver; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; -import com.sun.el.ExpressionFactoryImpl; - /** * Resource bundle backed message interpolator. * @@ -195,7 +195,10 @@ private static ExpressionFactory buildExpressionFactory() { // Finally we try the CL of the EL implementation itself. This is necessary for OSGi now that the // implementation is separated from the API. - SetContextClassLoader.action( ExpressionFactoryImpl.class.getClassLoader() ); + // Instead of running this: + // SetContextClassLoader.action( ExpressionFactoryImpl.class.getClassLoader() ); + // we do some reflection "magic" to not have a dependency on an implementation of the expression language: + SetContextClassLoader.action( classLoaderForExpressionFactory( originalContextClassLoader ) ); if ( canLoadExpressionFactory() ) { ExpressionFactory expressionFactory = ELManager.getExpressionFactory(); LOG.debug( "Loaded expression factory via com.sun.el classloader" ); @@ -213,6 +216,36 @@ private static ExpressionFactory buildExpressionFactory() { throw LOG.getUnableToInitializeELExpressionFactoryException( null ); } + /* + * In an OSGi environment we won't have access to the classloader that is capable to instantiate the EL factory from the get-go. + * Instead, we have to use the classloader that loaded some class from the EL implementation, e.g. ExpressionFactoryImpl. + * To get that classloader we list all the OSGi bundles through the OSGi BundleContext and go bundle by bundle to find the one + * that is able to load the ExpressionFactoryImpl class. + * + * We rely on reflection here as we do not have a dependency on OSGi in the engine module, and we do not want to add it! + */ + private static ClassLoader classLoaderForExpressionFactory(ClassLoader cl) throws Exception { + Class fu = cl.loadClass( "org.osgi.framework.FrameworkUtil" ); + Method getBundle = fu.getMethod( "getBundle", Class.class ); + Object currentBundle = getBundle.invoke( null, ResourceBundleMessageInterpolator.class ); + if ( currentBundle != null ) { + Object context = cl.loadClass( "org.osgi.framework.Bundle" ).getMethod( "getBundleContext" ).invoke( currentBundle ); + Object bundles = cl.loadClass( "org.osgi.framework.BundleContext" ).getMethod( "getBundles" ).invoke( context ); + Method loadClass = cl.loadClass( "org.osgi.framework.Bundle" ).getMethod( "loadClass", String.class ); + int n = Array.getLength( bundles ); + for ( int i = 0; i < n; i++ ) { + try { + Object bundle = Array.get( bundles, i ); + return ( (Class) loadClass.invoke( bundle, "com.sun.el.ExpressionFactoryImpl" ) ).getClassLoader(); + } + catch (Exception e) { + // + } + } + } + return null; + } + /** * Instead of testing the different class loaders via {@link ELManager}, we directly access the * {@link ExpressionFactory}. This avoids issues with loading the {@code ELUtil} class (used by {@code ELManager})