Permalink
Browse files

HV-853 Provide a NonElMessageInterpolator

  • Loading branch information...
1 parent 9f50adc commit 9b676a662625d85d62e7551392c9d31788ae875f @stawny stawny committed with hferentschik Jun 30, 2014
@@ -57,6 +57,13 @@ implementation]:
----
====
+[TIP]
+====
+For environments where one cannot provide a EL implementation Hibernate Validator is offering a
+<<non-el-message-interpolator>>. However, the use of this interpolator
+is not Bean Validation specification compliant.
+====
+
[[section-getting-started-cdi]]
==== CDI
@@ -354,12 +354,29 @@ Using _$$ALL_FALSE$$_ as composition type implicitly enforces that only a single
reported in case validation of the constraint composition fails.
====
-=== ResourceBundleLocator
+[[non-el-message-interpolator]]
+=== NonElMessageInterpolator
+
+Hibernate Validator requires per default an implementation of the Unified EL (see
+<<validator-gettingstarted-uel>>) to be available. This is needed to allow the interpolation
+of constraint error messages using EL expressions as defined by Bean Validation 1.1.
+
+For environments where you cannot or do not want to provide an EL implementation, Hibernate Validators
+offers a non EL based message interpolator - +org.hibernate.validator.messageinterpolation.NonElMessageInterpolator+.
-As described in <<section-custom-message-interpolation>>, Bean Validation allows to plug in custom
-message interpolator implementations.
+Refer to <<section-custom-message-interpolation>> to see how to plug in custom message interpolator
+implementations.
+
+[WARNING]
+====
+Constraint messages containing EL expressions will be returned un-inerpolated by
@gunnarmorling
gunnarmorling Jun 30, 2014 Member

un-interpolated.

++org.hibernate.validator.messageinterpolation.NonElMessageInterpolator+. This also affects potentially
+built-in default constraint messages, for example the ones from +DecimalMin+ or +DecimalMax+.
@gunnarmorling
gunnarmorling Jun 30, 2014 Member

Are there any others besides those two?

+====
+
+=== ResourceBundleLocator
-With ResourceBundleLocator, Hibernate Validator provides an additional SPI which allows to retrieve
+With +ResourceBundleLocator+, Hibernate Validator provides an additional SPI which allows to retrieve
error messages from other resource bundles than _ValidationMessages_ while still using the actual
interpolation algorithm as defined by the specification. Refer to
<<section-resource-bundle-locator>> to learn how to make use of that SPI.
@@ -44,6 +44,7 @@
import org.hibernate.validator.internal.util.TypeResolutionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
+import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper;
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
@@ -245,6 +246,18 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
ParameterNameProvider parameterNameProvider,
boolean failFast,
List<ValidatedValueUnwrapper<?>> validatedValueHandlers) {
+
+ // HV-793 - To fail eagerly in case we have no EL dependencies on the classpath we try to load the expression
+ // factory
+ if( messageInterpolator instanceof ResourceBundleMessageInterpolator ) {
+ try {
+ ResourceBundleMessageInterpolator.class.getClassLoader().loadClass( "javax.el.ExpressionFactory" );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw log.getMissingELDependenciesException();
+ }
+ }
+
BeanMetaDataManager beanMetaDataManager;
if ( !beanMetaDataManagerMap.containsKey( parameterNameProvider ) ) {
beanMetaDataManager = new BeanMetaDataManager(
@@ -0,0 +1,121 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.hibernate.validator.internal.engine.messageinterpolation;
+
+import java.util.Locale;
+import java.util.Map;
+
+import javax.el.ELException;
+import javax.el.ExpressionFactory;
+import javax.el.PropertyNotFoundException;
+import javax.el.ValueExpression;
+import javax.validation.MessageInterpolator;
+
+import org.hibernate.validator.internal.engine.MessageInterpolatorContext;
+import org.hibernate.validator.internal.engine.messageinterpolation.el.RootResolver;
+import org.hibernate.validator.internal.engine.messageinterpolation.el.SimpleELContext;
+import org.hibernate.validator.internal.util.logging.Log;
+import org.hibernate.validator.internal.util.logging.LoggerFactory;
+
+/**
+ * Resolver for the el expressions.
+ *
+ * @author Hardy Ferentschik
+ * @author Adam Stawicki
+ */
+public class ElTermResolver implements TermResolver {
+ private static final Log log = LoggerFactory.make();
+
+ /**
+ * Name under which the currently validate value is bound to the EL context.
+ */
+ private static final String VALIDATED_VALUE_NAME = "validatedValue";
+
+ /**
+ * The locale for which to interpolate the expression.
+ */
+ private final Locale locale;
+
+ /**
+ * Factory for creating EL expressions
+ */
+ private static final ExpressionFactory expressionFactory;
+
+ static {
+ expressionFactory = ExpressionFactory.newInstance();
+ }
+
+ public ElTermResolver(Locale locale) {
+ this.locale = locale;
+ }
+
+ @Override
+ public String interpolate(MessageInterpolator.Context context, String expression) {
+ String resolvedExpression = expression;
+ SimpleELContext elContext = new SimpleELContext();
+ try {
+ ValueExpression valueExpression = bindContextValues( expression, context, elContext );
+ resolvedExpression = (String) valueExpression.getValue( elContext );
+ }
+ catch ( PropertyNotFoundException pnfe ) {
+ log.unknownPropertyInExpressionLanguage( expression, pnfe );
+ }
+ catch ( ELException e ) {
+ log.errorInExpressionLanguage( expression, e );
+ }
+ catch ( Exception e ) {
+ log.evaluatingExpressionLanguageExpressionCausedException( expression, e );
+ }
+
+ return resolvedExpression;
+ }
+
+ private ValueExpression bindContextValues(String messageTemplate, MessageInterpolator.Context messageInterpolatorContext, SimpleELContext elContext) {
+ // bind the validated value
+ ValueExpression valueExpression = expressionFactory.createValueExpression(
+ messageInterpolatorContext.getValidatedValue(),
+ Object.class
+ );
+ elContext.setVariable( VALIDATED_VALUE_NAME, valueExpression );
+
+ // bind a formatter instantiated with proper locale
+ valueExpression = expressionFactory.createValueExpression(
+ new FormatterWrapper( locale ),
+ FormatterWrapper.class
+ );
+ elContext.setVariable( RootResolver.FORMATTER, valueExpression );
+
+ // map the annotation values
+ for ( Map.Entry<String, Object> entry : messageInterpolatorContext.getConstraintDescriptor()
+ .getAttributes()
+ .entrySet() ) {
+ valueExpression = expressionFactory.createValueExpression( entry.getValue(), Object.class );
+ elContext.setVariable( entry.getKey(), valueExpression );
+ }
+
+ // check for custom parameters provided by HibernateConstraintValidatorContext
+ if ( messageInterpolatorContext instanceof MessageInterpolatorContext ) {
+ MessageInterpolatorContext internalContext = (MessageInterpolatorContext) messageInterpolatorContext;
+ for ( Map.Entry<String, Object> entry : internalContext.getMessageParameters().entrySet() ) {
+ valueExpression = expressionFactory.createValueExpression( entry.getValue(), Object.class );
+ elContext.setVariable( entry.getKey(), valueExpression );
+ }
+ }
+
+ return expressionFactory.createValueExpression( elContext, messageTemplate, String.class );
+ }
+}
@@ -16,20 +16,9 @@
*/
package org.hibernate.validator.internal.engine.messageinterpolation;
-import java.util.Arrays;
import java.util.Locale;
-import java.util.Map;
-import javax.el.ELException;
-import javax.el.ExpressionFactory;
-import javax.el.PropertyNotFoundException;
-import javax.el.ValueExpression;
-import javax.validation.MessageInterpolator;
-import org.hibernate.validator.internal.engine.MessageInterpolatorContext;
-import org.hibernate.validator.internal.engine.messageinterpolation.el.RootResolver;
-import org.hibernate.validator.internal.engine.messageinterpolation.el.SimpleELContext;
-import org.hibernate.validator.internal.util.logging.Log;
-import org.hibernate.validator.internal.util.logging.LoggerFactory;
+import javax.validation.MessageInterpolator;
/**
* Helper class dealing with the interpolation of a single message parameter or expression extracted from a message
@@ -38,28 +27,12 @@
* @author Hardy Ferentschik
*/
public class InterpolationTerm {
- private static final Log log = LoggerFactory.make();
-
- /**
- * Name under which the currently validate value is bound to the EL context.
- */
- private static final String VALIDATED_VALUE_NAME = "validatedValue";
-
/**
* Meta character to designate an EL expression.
*/
private static final String EL_DESIGNATION_CHARACTER = "$";
/**
- * Factory for creating EL expressions
- */
- private static final ExpressionFactory expressionFactory;
-
- static {
- expressionFactory = ExpressionFactory.newInstance();
- }
-
- /**
* The actual expression (parameter or EL expression).
*/
private final String expression;
@@ -70,28 +43,28 @@
private final InterpolationTermType type;
/**
- * The locale for which to interpolate the expression.
+ * The resolver for the expression.
*/
- private final Locale locale;
+ private final TermResolver resolver;
public InterpolationTerm(String expression, Locale locale) {
- this.locale = locale;
this.expression = expression;
- if ( expression.startsWith( EL_DESIGNATION_CHARACTER ) ) {
+ if ( isElExpression(expression) ) {
this.type = InterpolationTermType.EL;
+ this.resolver = new ElTermResolver(locale);
}
else {
this.type = InterpolationTermType.PARAMETER;
+ this.resolver = new ParameterTermResolver();
}
}
+ public static boolean isElExpression(String expression) {
+ return expression.startsWith( EL_DESIGNATION_CHARACTER );
+ }
+
public String interpolate(MessageInterpolator.Context context) {
- if ( InterpolationTermType.EL.equals( type ) ) {
- return interpolateExpressionLanguageTerm( context );
- }
- else {
- return interpolateConstraintAnnotationValue( context );
- }
+ return resolver.interpolate( context, expression );
}
@Override
@@ -103,84 +76,6 @@ public String toString() {
sb.append( '}' );
return sb.toString();
}
-
- private String interpolateExpressionLanguageTerm(MessageInterpolator.Context context) {
- String resolvedExpression = expression;
- SimpleELContext elContext = new SimpleELContext();
- try {
- ValueExpression valueExpression = bindContextValues( expression, context, elContext );
- resolvedExpression = (String) valueExpression.getValue( elContext );
- }
- catch ( PropertyNotFoundException pnfe ) {
- log.unknownPropertyInExpressionLanguage( expression, pnfe );
- }
- catch ( ELException e ) {
- log.errorInExpressionLanguage( expression, e );
- }
- catch ( Exception e ) {
- log.evaluatingExpressionLanguageExpressionCausedException( expression, e );
- }
-
- return resolvedExpression;
- }
-
- private String interpolateConstraintAnnotationValue(MessageInterpolator.Context context) {
- String resolvedExpression;
- Object variable = context.getConstraintDescriptor()
- .getAttributes()
- .get( removeCurlyBraces( expression ) );
- if ( variable != null ) {
- if ( variable.getClass().isArray() ) {
- resolvedExpression = Arrays.toString( (Object[]) variable );
- }
- else {
- resolvedExpression = variable.toString();
- }
- }
- else {
- resolvedExpression = expression;
- }
- return resolvedExpression;
- }
-
- private String removeCurlyBraces(String parameter) {
- return parameter.substring( 1, parameter.length() - 1 );
- }
-
- private ValueExpression bindContextValues(String messageTemplate, MessageInterpolator.Context messageInterpolatorContext, SimpleELContext elContext) {
- // bind the validated value
- ValueExpression valueExpression = expressionFactory.createValueExpression(
- messageInterpolatorContext.getValidatedValue(),
- Object.class
- );
- elContext.setVariable( VALIDATED_VALUE_NAME, valueExpression );
-
- // bind a formatter instantiated with proper locale
- valueExpression = expressionFactory.createValueExpression(
- new FormatterWrapper( locale ),
- FormatterWrapper.class
- );
- elContext.setVariable( RootResolver.FORMATTER, valueExpression );
-
- // map the annotation values
- for ( Map.Entry<String, Object> entry : messageInterpolatorContext.getConstraintDescriptor()
- .getAttributes()
- .entrySet() ) {
- valueExpression = expressionFactory.createValueExpression( entry.getValue(), Object.class );
- elContext.setVariable( entry.getKey(), valueExpression );
- }
-
- // check for custom parameters provided by HibernateConstraintValidatorContext
- if ( messageInterpolatorContext instanceof MessageInterpolatorContext ) {
- MessageInterpolatorContext internalContext = (MessageInterpolatorContext) messageInterpolatorContext;
- for ( Map.Entry<String, Object> entry : internalContext.getMessageParameters().entrySet() ) {
- valueExpression = expressionFactory.createValueExpression( entry.getValue(), Object.class );
- elContext.setVariable( entry.getKey(), valueExpression );
- }
- }
-
- return expressionFactory.createValueExpression( elContext, messageTemplate, String.class );
- }
}
Oops, something went wrong.

2 comments on commit 9b676a6

@gunnarmorling
Member

Do we actually have a test for using the parameter-only interpolator in an environment without EL? I can't find it but I might just be missing it.

Please sign in to comment.