55package org .hibernate .validator .messageinterpolation ;
66
77import java .lang .invoke .MethodHandles ;
8+ import java .lang .reflect .Array ;
9+ import java .lang .reflect .Method ;
810import java .util .Collections ;
911import java .util .Locale ;
1012import java .util .Set ;
2224import org .hibernate .validator .spi .messageinterpolation .LocaleResolver ;
2325import org .hibernate .validator .spi .resourceloading .ResourceBundleLocator ;
2426
25- import com .sun .el .ExpressionFactoryImpl ;
26-
2727/**
2828 * Resource bundle backed message interpolator.
2929 *
@@ -195,7 +195,10 @@ private static ExpressionFactory buildExpressionFactory() {
195195
196196 // Finally we try the CL of the EL implementation itself. This is necessary for OSGi now that the
197197 // implementation is separated from the API.
198- SetContextClassLoader .action ( ExpressionFactoryImpl .class .getClassLoader () );
198+ // Instead of running this:
199+ // SetContextClassLoader.action( ExpressionFactoryImpl.class.getClassLoader() );
200+ // we do some reflection "magic" to not have a dependency on an implementation of the expression language:
201+ SetContextClassLoader .action ( classLoaderForExpressionFactory ( originalContextClassLoader ) );
199202 if ( canLoadExpressionFactory () ) {
200203 ExpressionFactory expressionFactory = ELManager .getExpressionFactory ();
201204 LOG .debug ( "Loaded expression factory via com.sun.el classloader" );
@@ -213,6 +216,36 @@ private static ExpressionFactory buildExpressionFactory() {
213216 throw LOG .getUnableToInitializeELExpressionFactoryException ( null );
214217 }
215218
219+ /*
220+ * In an OSGi environment we won't have access to the classloader that is capable to instantiate the EL factory from the get-go.
221+ * Instead, we have to use the classloader that loaded some class from the EL implementation, e.g. ExpressionFactoryImpl.
222+ * To get that classloader we list all the OSGi bundles through the OSGi BundleContext and go bundle by bundle to find the one
223+ * that is able to load the ExpressionFactoryImpl class.
224+ *
225+ * 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!
226+ */
227+ private static ClassLoader classLoaderForExpressionFactory (ClassLoader cl ) throws Exception {
228+ Class <?> fu = cl .loadClass ( "org.osgi.framework.FrameworkUtil" );
229+ Method getBundle = fu .getMethod ( "getBundle" , Class .class );
230+ Object currentBundle = getBundle .invoke ( null , ResourceBundleMessageInterpolator .class );
231+ if ( currentBundle != null ) {
232+ Object context = cl .loadClass ( "org.osgi.framework.Bundle" ).getMethod ( "getBundleContext" ).invoke ( currentBundle );
233+ Object bundles = cl .loadClass ( "org.osgi.framework.BundleContext" ).getMethod ( "getBundles" ).invoke ( context );
234+ Method loadClass = cl .loadClass ( "org.osgi.framework.Bundle" ).getMethod ( "loadClass" , String .class );
235+ int n = Array .getLength ( bundles );
236+ for ( int i = 0 ; i < n ; i ++ ) {
237+ try {
238+ Object bundle = Array .get ( bundles , i );
239+ return ( (Class <?>) loadClass .invoke ( bundle , "com.sun.el.ExpressionFactoryImpl" ) ).getClassLoader ();
240+ }
241+ catch (Exception e ) {
242+ //
243+ }
244+ }
245+ }
246+ return null ;
247+ }
248+
216249 /**
217250 * Instead of testing the different class loaders via {@link ELManager}, we directly access the
218251 * {@link ExpressionFactory}. This avoids issues with loading the {@code ELUtil} class (used by {@code ELManager})
0 commit comments