Skip to content

Commit 2e7ed91

Browse files
committed
Merge branch '5.1.x'
2 parents cf5d0e6 + a089027 commit 2e7ed91

File tree

12 files changed

+229
-69
lines changed

12 files changed

+229
-69
lines changed

spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/SpringValidatorAdapterTests.java

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
import javax.validation.Constraint;
3535
import javax.validation.ConstraintValidator;
3636
import javax.validation.ConstraintValidatorContext;
37+
import javax.validation.ConstraintViolation;
3738
import javax.validation.Payload;
3839
import javax.validation.Valid;
3940
import javax.validation.Validation;
@@ -50,11 +51,13 @@
5051
import org.springframework.context.support.StaticMessageSource;
5152
import org.springframework.util.ObjectUtils;
5253
import org.springframework.validation.BeanPropertyBindingResult;
54+
import org.springframework.validation.FieldError;
5355
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
5456

5557
import static java.lang.annotation.ElementType.*;
5658
import static java.lang.annotation.RetentionPolicy.*;
5759
import static org.hamcrest.core.Is.*;
60+
import static org.hamcrest.core.StringContains.*;
5861
import static org.junit.Assert.*;
5962

6063
/**
@@ -73,7 +76,7 @@ public class SpringValidatorAdapterTests {
7376
@Before
7477
public void setupSpringValidatorAdapter() {
7578
messageSource.addMessage("Size", Locale.ENGLISH, "Size of {0} is must be between {2} and {1}");
76-
messageSource.addMessage("Same", Locale.ENGLISH, "{2} must be same value with {1}");
79+
messageSource.addMessage("Same", Locale.ENGLISH, "{2} must be same value as {1}");
7780
messageSource.addMessage("password", Locale.ENGLISH, "Password");
7881
messageSource.addMessage("confirmPassword", Locale.ENGLISH, "Password(Confirm)");
7982
}
@@ -96,8 +99,11 @@ public void testNoStringArgumentValue() {
9699

97100
assertThat(errors.getFieldErrorCount("password"), is(1));
98101
assertThat(errors.getFieldValue("password"), is("pass"));
99-
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
100-
is("Size of Password is must be between 8 and 128"));
102+
FieldError error = errors.getFieldError("password");
103+
assertNotNull(error);
104+
assertThat(messageSource.getMessage(error, Locale.ENGLISH), is("Size of Password is must be between 8 and 128"));
105+
assertTrue(error.contains(ConstraintViolation.class));
106+
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("password"));
101107
}
102108

103109
@Test // SPR-13406
@@ -111,8 +117,11 @@ public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLog
111117

112118
assertThat(errors.getFieldErrorCount("password"), is(1));
113119
assertThat(errors.getFieldValue("password"), is("password"));
114-
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
115-
is("Password must be same value with Password(Confirm)"));
120+
FieldError error = errors.getFieldError("password");
121+
assertNotNull(error);
122+
assertThat(messageSource.getMessage(error, Locale.ENGLISH), is("Password must be same value as Password(Confirm)"));
123+
assertTrue(error.contains(ConstraintViolation.class));
124+
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("password"));
116125
}
117126

118127
@Test // SPR-13406
@@ -127,10 +136,16 @@ public void testApplyMessageSourceResolvableToStringArgumentValueWithUnresolvedL
127136
assertThat(errors.getFieldErrorCount("email"), is(1));
128137
assertThat(errors.getFieldValue("email"), is("[email protected]"));
129138
assertThat(errors.getFieldErrorCount("confirmEmail"), is(1));
130-
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
131-
is("email must be same value with confirmEmail"));
132-
assertThat(messageSource.getMessage(errors.getFieldError("confirmEmail"), Locale.ENGLISH),
133-
is("Email required"));
139+
FieldError error1 = errors.getFieldError("email");
140+
FieldError error2 = errors.getFieldError("confirmEmail");
141+
assertNotNull(error1);
142+
assertNotNull(error2);
143+
assertThat(messageSource.getMessage(error1, Locale.ENGLISH), is("email must be same value as confirmEmail"));
144+
assertThat(messageSource.getMessage(error2, Locale.ENGLISH), is("Email required"));
145+
assertTrue(error1.contains(ConstraintViolation.class));
146+
assertThat(error1.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
147+
assertTrue(error2.contains(ConstraintViolation.class));
148+
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("confirmEmail"));
134149
}
135150

136151
@Test // SPR-15123
@@ -147,10 +162,34 @@ public void testApplyMessageSourceResolvableToStringArgumentValueWithAlwaysUseMe
147162
assertThat(errors.getFieldErrorCount("email"), is(1));
148163
assertThat(errors.getFieldValue("email"), is("[email protected]"));
149164
assertThat(errors.getFieldErrorCount("confirmEmail"), is(1));
150-
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
151-
is("email must be same value with confirmEmail"));
152-
assertThat(messageSource.getMessage(errors.getFieldError("confirmEmail"), Locale.ENGLISH),
153-
is("Email required"));
165+
FieldError error1 = errors.getFieldError("email");
166+
FieldError error2 = errors.getFieldError("confirmEmail");
167+
assertNotNull(error1);
168+
assertNotNull(error2);
169+
assertThat(messageSource.getMessage(error1, Locale.ENGLISH), is("email must be same value as confirmEmail"));
170+
assertThat(messageSource.getMessage(error2, Locale.ENGLISH), is("Email required"));
171+
assertTrue(error1.contains(ConstraintViolation.class));
172+
assertThat(error1.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
173+
assertTrue(error2.contains(ConstraintViolation.class));
174+
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("confirmEmail"));
175+
}
176+
177+
@Test
178+
public void testPatternMessage() {
179+
TestBean testBean = new TestBean();
180+
testBean.setEmail("X");
181+
testBean.setConfirmEmail("X");
182+
183+
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
184+
validatorAdapter.validate(testBean, errors);
185+
186+
assertThat(errors.getFieldErrorCount("email"), is(1));
187+
assertThat(errors.getFieldValue("email"), is("X"));
188+
FieldError error = errors.getFieldError("email");
189+
assertNotNull(error);
190+
assertThat(messageSource.getMessage(error, Locale.ENGLISH), containsString("[\\w.'-]{1,}@[\\w.'-]{1,}"));
191+
assertTrue(error.contains(ConstraintViolation.class));
192+
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
154193
}
155194

156195
@Test // SPR-16177
@@ -243,6 +282,7 @@ static class TestBean {
243282

244283
private String confirmPassword;
245284

285+
@Pattern(regexp = "[\\w.'-]{1,}@[\\w.'-]{1,}")
246286
private String email;
247287

248288
@Pattern(regexp = "[\\p{L} -]*", message = "Email required")
@@ -403,13 +443,13 @@ public static class Child {
403443

404444
private Integer id;
405445

406-
@javax.validation.constraints.NotNull
446+
@NotNull
407447
private String name;
408448

409-
@javax.validation.constraints.NotNull
449+
@NotNull
410450
private Integer age;
411451

412-
@javax.validation.constraints.NotNull
452+
@NotNull
413453
private Parent parent;
414454

415455
public Integer getId() {

spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/ValidatorFactoryTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -284,6 +284,9 @@ public void testListValidation() {
284284
errors.initConversion(new DefaultConversionService());
285285
validator.validate(listContainer, errors);
286286

287+
FieldError fieldError = errors.getFieldError("list[1]");
288+
assertNotNull(fieldError);
289+
assertEquals("X", fieldError.getRejectedValue());
287290
assertEquals("X", errors.getFieldValue("list[1]"));
288291
}
289292

spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,9 +57,9 @@ public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
5757
@Override
5858
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
5959
boolean candidateFound = false;
60-
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
61-
for (String annoType : annoTypes) {
62-
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
60+
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
61+
for (String annType : annTypes) {
62+
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
6363
if (candidate == null) {
6464
continue;
6565
}

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -534,16 +534,14 @@ private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, S
534534
if (visited.add(sourceClass)) {
535535
for (SourceClass annotation : sourceClass.getAnnotations()) {
536536
String annName = annotation.getMetadata().getClassName();
537-
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
537+
if (!annName.equals(Import.class.getName())) {
538538
collectImports(annotation, imports, visited);
539539
}
540540
}
541541
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
542542
}
543543
}
544544

545-
546-
547545
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
548546
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
549547

@@ -565,8 +563,7 @@ private void processImports(ConfigurationClass configClass, SourceClass currentS
565563
ParserStrategyUtils.invokeAwareMethods(
566564
selector, this.environment, this.resourceLoader, this.registry);
567565
if (selector instanceof DeferredImportSelector) {
568-
this.deferredImportSelectorHandler.handle(
569-
configClass, (DeferredImportSelector) selector);
566+
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
570567
}
571568
else {
572569
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
@@ -1018,13 +1015,32 @@ public Set<SourceClass> getInterfaces() throws IOException {
10181015

10191016
public Set<SourceClass> getAnnotations() {
10201017
Set<SourceClass> result = new LinkedHashSet<>();
1021-
for (String className : this.metadata.getAnnotationTypes()) {
1022-
try {
1023-
result.add(getRelated(className));
1018+
if (this.source instanceof Class) {
1019+
Class<?> sourceClass = (Class<?>) this.source;
1020+
for (Annotation ann : sourceClass.getAnnotations()) {
1021+
Class<?> annType = ann.annotationType();
1022+
if (!annType.getName().startsWith("java")) {
1023+
try {
1024+
result.add(asSourceClass(annType));
1025+
}
1026+
catch (Throwable ex) {
1027+
// An annotation not present on the classpath is being ignored
1028+
// by the JVM's class loading -> ignore here as well.
1029+
}
1030+
}
10241031
}
1025-
catch (Throwable ex) {
1026-
// An annotation not present on the classpath is being ignored
1027-
// by the JVM's class loading -> ignore here as well.
1032+
}
1033+
else {
1034+
for (String className : this.metadata.getAnnotationTypes()) {
1035+
if (!className.startsWith("java")) {
1036+
try {
1037+
result.add(getRelated(className));
1038+
}
1039+
catch (Throwable ex) {
1040+
// An annotation not present on the classpath is being ignored
1041+
// by the JVM's class loading -> ignore here as well.
1042+
}
1043+
}
10281044
}
10291045
}
10301046
return result;

spring-context/src/main/java/org/springframework/context/support/AbstractMessageSource.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -287,6 +287,12 @@ protected String getDefaultMessage(MessageSourceResolvable resolvable, Locale lo
287287
String defaultMessage = resolvable.getDefaultMessage();
288288
String[] codes = resolvable.getCodes();
289289
if (defaultMessage != null) {
290+
if (resolvable instanceof DefaultMessageSourceResolvable &&
291+
!((DefaultMessageSourceResolvable) resolvable).shouldRenderDefaultMessage()) {
292+
// Given default message does not contain any argument placeholders
293+
// (and isn't escaped for alwaysUseMessageFormat either) -> return as-is.
294+
return defaultMessage;
295+
}
290296
if (!ObjectUtils.isEmpty(codes) && defaultMessage.equals(codes[0])) {
291297
// Never format a code-as-default-message, even with alwaysUseMessageFormat=true
292298
return defaultMessage;

spring-context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -129,13 +129,28 @@ public String getDefaultMessage() {
129129
return this.defaultMessage;
130130
}
131131

132+
/**
133+
* Indicate whether the specified default message needs to be rendered for
134+
* substituting placeholders and/or {@link java.text.MessageFormat} escaping.
135+
* @return {@code true} if the default message may contain argument placeholders;
136+
* {@code false} if it definitely does not contain placeholders or custom escaping
137+
* and can therefore be simply exposed as-is
138+
* @since 5.1.7
139+
* @see #getDefaultMessage()
140+
* @see #getArguments()
141+
* @see AbstractMessageSource#renderDefaultMessage
142+
*/
143+
public boolean shouldRenderDefaultMessage() {
144+
return true;
145+
}
146+
132147

133148
/**
134149
* Build a default String representation for this MessageSourceResolvable:
135150
* including codes, arguments, and default message.
136151
*/
137152
protected final String resolvableToString() {
138-
StringBuilder result = new StringBuilder();
153+
StringBuilder result = new StringBuilder(64);
139154
result.append("codes [").append(StringUtils.arrayToDelimitedString(this.codes, ","));
140155
result.append("]; arguments [").append(StringUtils.arrayToDelimitedString(this.arguments, ","));
141156
result.append("]; default message [").append(this.defaultMessage).append(']');

spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -148,6 +148,7 @@ private Class<?>[] asValidationGroups(Object... validationHints) {
148148
* @param violations the JSR-303 ConstraintViolation results
149149
* @param errors the Spring errors object to register to
150150
*/
151+
@SuppressWarnings("serial")
151152
protected void processConstraintViolations(Set<ConstraintViolation<Object>> violations, Errors errors) {
152153
for (ConstraintViolation<Object> violation : violations) {
153154
String field = determineField(violation);
@@ -165,15 +166,25 @@ protected void processConstraintViolations(Set<ConstraintViolation<Object>> viol
165166
if (nestedField.isEmpty()) {
166167
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
167168
ObjectError error = new ObjectError(
168-
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage());
169+
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()) {
170+
@Override
171+
public boolean shouldRenderDefaultMessage() {
172+
return false;
173+
}
174+
};
169175
error.wrap(violation);
170176
bindingResult.addError(error);
171177
}
172178
else {
173179
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
174180
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
175181
FieldError error = new FieldError(errors.getObjectName(), nestedField,
176-
rejectedValue, false, errorCodes, errorArgs, violation.getMessage());
182+
rejectedValue, false, errorCodes, errorArgs, violation.getMessage()) {
183+
@Override
184+
public boolean shouldRenderDefaultMessage() {
185+
return false;
186+
}
187+
};
177188
error.wrap(violation);
178189
bindingResult.addError(error);
179190
}

0 commit comments

Comments
 (0)