55package org .hibernate .validator .messageinterpolation ;
66
77import java .lang .invoke .MethodHandles ;
8+ import java .lang .reflect .Array ;
9+ import java .lang .reflect .InvocationTargetException ;
10+ import java .lang .reflect .Method ;
811import java .util .Collections ;
912import java .util .Locale ;
1013import java .util .Set ;
2225import org .hibernate .validator .spi .messageinterpolation .LocaleResolver ;
2326import org .hibernate .validator .spi .resourceloading .ResourceBundleLocator ;
2427
25- import com .sun .el .ExpressionFactoryImpl ;
26-
2728/**
2829 * Resource bundle backed message interpolator.
2930 *
@@ -195,7 +196,10 @@ private static ExpressionFactory buildExpressionFactory() {
195196
196197 // Finally we try the CL of the EL implementation itself. This is necessary for OSGi now that the
197198 // implementation is separated from the API.
198- SetContextClassLoader .action ( ExpressionFactoryImpl .class .getClassLoader () );
199+ // Instead of running this:
200+ // SetContextClassLoader.action( ExpressionFactoryImpl.class.getClassLoader() );
201+ // we do some reflection "magic" to not have a dependency on an implementation of the expression language:
202+ SetContextClassLoader .action ( classLoaderForExpressionFactory ( originalContextClassLoader ) );
199203 if ( canLoadExpressionFactory () ) {
200204 ExpressionFactory expressionFactory = ELManager .getExpressionFactory ();
201205 LOG .debug ( "Loaded expression factory via com.sun.el classloader" );
@@ -213,6 +217,41 @@ private static ExpressionFactory buildExpressionFactory() {
213217 throw LOG .getUnableToInitializeELExpressionFactoryException ( null );
214218 }
215219
220+ /*
221+ * In an OSGi environment we won't have access to the classloader that is capable to instantiate the EL factory from the get-go.
222+ * Instead, we have to use the classloader that loaded some class from the EL implementation, e.g. ExpressionFactoryImpl.
223+ * To get that classloader we list all the OSGi bundles through the OSGi BundleContext and go bundle by bundle to find the one
224+ * that is able to load the ExpressionFactoryImpl class.
225+ *
226+ * 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!
227+ */
228+ private static ClassLoader classLoaderForExpressionFactory (ClassLoader cl ) throws Exception {
229+ try {
230+ Class <?> fu = cl .loadClass ( "org.osgi.framework.FrameworkUtil" );
231+ Method getBundle = fu .getMethod ( "getBundle" , Class .class );
232+ Object currentBundle = getBundle .invoke ( null , ResourceBundleMessageInterpolator .class );
233+ if ( currentBundle != null ) {
234+ Object context = cl .loadClass ( "org.osgi.framework.Bundle" ).getMethod ( "getBundleContext" ).invoke ( currentBundle );
235+ Object bundles = cl .loadClass ( "org.osgi.framework.BundleContext" ).getMethod ( "getBundles" ).invoke ( context );
236+ Method loadClass = cl .loadClass ( "org.osgi.framework.Bundle" ).getMethod ( "loadClass" , String .class );
237+ int n = Array .getLength ( bundles );
238+ for ( int i = 0 ; i < n ; i ++ ) {
239+ try {
240+ Object bundle = Array .get ( bundles , i );
241+ return ( (Class <?>) loadClass .invoke ( bundle , "com.sun.el.ExpressionFactoryImpl" ) ).getClassLoader ();
242+ }
243+ catch (Exception e ) {
244+ //
245+ }
246+ }
247+ }
248+ }
249+ catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) {
250+ throw e ;
251+ }
252+ return null ;
253+ }
254+
216255 /**
217256 * Instead of testing the different class loaders via {@link ELManager}, we directly access the
218257 * {@link ExpressionFactory}. This avoids issues with loading the {@code ELUtil} class (used by {@code ELManager})
0 commit comments