Skip to content

Commit aa57bbf

Browse files
committed
HV-2020 Use OSGi framework utils to get a classloader
and do not rely on a dependency to expressly
1 parent 1793bef commit aa57bbf

File tree

2 files changed

+47
-9
lines changed

2 files changed

+47
-9
lines changed

engine/pom.xml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,6 @@
7474
<scope>provided</scope>
7575
<optional>true</optional>
7676
</dependency>
77-
<dependency>
78-
<groupId>org.glassfish.expressly</groupId>
79-
<artifactId>expressly</artifactId>
80-
<scope>provided</scope>
81-
<optional>true</optional>
82-
</dependency>
8377
<dependency>
8478
<groupId>org.jboss.logging</groupId>
8579
<artifactId>jboss-logging-annotations</artifactId>
@@ -125,6 +119,11 @@
125119
<!--
126120
Test dependencies
127121
-->
122+
<dependency>
123+
<groupId>org.glassfish.expressly</groupId>
124+
<artifactId>expressly</artifactId>
125+
<scope>test</scope>
126+
</dependency>
128127
<dependency>
129128
<groupId>org.testng</groupId>
130129
<artifactId>testng</artifactId>

engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
package org.hibernate.validator.messageinterpolation;
66

77
import java.lang.invoke.MethodHandles;
8+
import java.lang.reflect.Array;
9+
import java.lang.reflect.InvocationTargetException;
10+
import java.lang.reflect.Method;
811
import java.util.Collections;
912
import java.util.Locale;
1013
import java.util.Set;
@@ -22,8 +25,6 @@
2225
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
2326
import 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

Comments
 (0)