Skip to content

Commit 7770c23

Browse files
committed
Use ParameterMessageInterpolator to bypass EL discovery in OSGi
ServiceLoader-based EL ExpressionFactory discovery does not work across OSGi bundle boundaries, and DynamicImport-Package does not help because the wrapped HV bundle cannot resolve the Expressly implementation class at runtime. Use ParameterMessageInterpolator as the default MessageInterpolator in OSGi. This is Hibernate Validator's recommended fallback for environments where Jakarta EL is not available. Parameter interpolation ({0}, {1}) still works; only EL expressions (${...}) are not supported.
1 parent c65db78 commit 7770c23

2 files changed

Lines changed: 12 additions & 61 deletions

File tree

components/camel-bean-validator/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,6 @@
8181
<version>${hibernate-validator-version}</version>
8282
<scope>provided</scope>
8383
</dependency>
84-
<dependency>
85-
<groupId>jakarta.el</groupId>
86-
<artifactId>jakarta.el-api</artifactId>
87-
<version>${jakarta-el-api-version}</version>
88-
<scope>provided</scope>
89-
</dependency>
9084
<!-- osgi support -->
9185
<dependency>
9286
<groupId>org.osgi</groupId>

components/camel-bean-validator/src/main/java/org/apache/camel/component/bean/validator/ValidatorFactories.java

Lines changed: 12 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
*/
1717
package org.apache.camel.component.bean.validator;
1818

19-
import java.util.Collections;
20-
import java.util.Locale;
21-
22-
import jakarta.el.ExpressionFactory;
2319
import jakarta.validation.ConstraintValidatorFactory;
2420
import jakarta.validation.MessageInterpolator;
2521
import jakarta.validation.TraversableResolver;
@@ -31,22 +27,21 @@
3127
import org.apache.camel.support.CamelContextHelper;
3228
import org.hibernate.validator.HibernateValidator;
3329
import org.hibernate.validator.HibernateValidatorConfiguration;
34-
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
35-
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
30+
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
3631

3732
/**
3833
* OSGi-aware override of the upstream ValidatorFactories.
3934
*
4035
* In OSGi, Hibernate Validator 9.x's {@code ResourceBundleMessageInterpolator}
4136
* tries to discover the Jakarta EL {@code ExpressionFactory} via
4237
* {@code ServiceLoader} / TCCL. This fails in OSGi because no single
43-
* bundle classloader can find the Expressly SPI descriptor
44-
* ({@code META-INF/services/jakarta.el.ExpressionFactory}).
38+
* bundle classloader can resolve the Expressly SPI descriptor across
39+
* bundle boundaries.
4540
*
46-
* This override directly instantiates the Expressly {@code ExpressionFactory}
47-
* (resolved via HV's {@code DynamicImport-Package}) and injects it into a
48-
* {@code ResourceBundleMessageInterpolator}, completely bypassing the
49-
* ServiceLoader discovery that does not work across OSGi bundles.
41+
* This override uses {@code ParameterMessageInterpolator} as the default
42+
* message interpolator when no custom one is provided, bypassing the EL
43+
* dependency entirely. This is the approach recommended by Hibernate
44+
* Validator for environments where EL is not available.
5045
*/
5146
public final class ValidatorFactories {
5247

@@ -82,25 +77,14 @@ public static ValidatorFactory buildValidatorFactory(
8277
.configure()
8378
.externalClassLoader(hvCl);
8479

85-
// If no custom MessageInterpolator was provided, create one with
86-
// a directly-instantiated ExpressionFactory to bypass the broken
87-
// ServiceLoader discovery in OSGi.
80+
// In OSGi, EL-based message interpolation does not work because
81+
// ServiceLoader cannot discover ExpressionFactory across bundles.
82+
// Use ParameterMessageInterpolator as the default fallback.
8883
if (messageInterpolator == null) {
89-
ExpressionFactory ef = createExpressionFactory(hvCl);
90-
if (ef != null) {
91-
// Must use the 7-param constructor — the 3-param
92-
// (ResourceBundleLocator, boolean, ExpressionFactory)
93-
// constructor has a bug in HV 9.1.0 that ignores the
94-
// ExpressionFactory parameter and calls buildExpressionFactory().
95-
messageInterpolator = new ResourceBundleMessageInterpolator(
96-
null, Collections.emptySet(), Locale.getDefault(),
97-
createDefaultLocaleResolver(hvCl), true, false, ef);
98-
}
84+
messageInterpolator = new ParameterMessageInterpolator();
9985
}
86+
hvConfig.messageInterpolator(messageInterpolator);
10087

101-
if (messageInterpolator != null) {
102-
hvConfig.messageInterpolator(messageInterpolator);
103-
}
10488
if (traversableResolver != null) {
10589
hvConfig.traversableResolver(traversableResolver);
10690
}
@@ -113,31 +97,4 @@ public static ValidatorFactory buildValidatorFactory(
11397

11498
return hvConfig.buildValidatorFactory();
11599
}
116-
117-
/**
118-
* Directly instantiate the Expressly ExpressionFactory via HV's
119-
* DynamicImport-Package wiring, bypassing ServiceLoader entirely.
120-
*/
121-
private static ExpressionFactory createExpressionFactory(ClassLoader hvCl) {
122-
try {
123-
Class<?> implClass = hvCl.loadClass("org.glassfish.expressly.ExpressionFactoryImpl");
124-
return (ExpressionFactory) implClass.getDeclaredConstructor().newInstance();
125-
} catch (Exception e) {
126-
return null;
127-
}
128-
}
129-
130-
/**
131-
* Create the HV DefaultLocaleResolver via reflection to avoid importing
132-
* the internal package which is not exported by the HV bundle.
133-
*/
134-
private static LocaleResolver createDefaultLocaleResolver(ClassLoader hvCl) {
135-
try {
136-
Class<?> clazz = hvCl.loadClass(
137-
"org.hibernate.validator.internal.engine.messageinterpolation.DefaultLocaleResolver");
138-
return (LocaleResolver) clazz.getDeclaredConstructor().newInstance();
139-
} catch (Exception e) {
140-
return null;
141-
}
142-
}
143100
}

0 commit comments

Comments
 (0)