Skip to content

Commit 0bf4234

Browse files
authored
Merge branch 'spring-projects:main' into main
2 parents 8987ed1 + 64f93d5 commit 0bf4234

File tree

12 files changed

+108
-198
lines changed

12 files changed

+108
-198
lines changed

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -18,7 +18,6 @@
1818

1919
import java.util.Collection;
2020
import java.util.HashSet;
21-
import java.util.List;
2221
import java.util.Map;
2322
import java.util.Optional;
2423
import java.util.Set;
@@ -107,18 +106,22 @@ public static BeanRegistrationAotContribution processAheadOfTime(RegisteredBean
107106
Set<Class<?>> validatedClasses = new HashSet<>();
108107
Set<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses = new HashSet<>();
109108

110-
processAheadOfTime(beanClass, validatedClasses, constraintValidatorClasses);
109+
processAheadOfTime(beanClass, new HashSet<>(), validatedClasses, constraintValidatorClasses);
111110

112111
if (!validatedClasses.isEmpty() || !constraintValidatorClasses.isEmpty()) {
113112
return new AotContribution(validatedClasses, constraintValidatorClasses);
114113
}
115114
return null;
116115
}
117116

118-
private static void processAheadOfTime(Class<?> clazz, Collection<Class<?>> validatedClasses,
119-
Collection<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
117+
private static void processAheadOfTime(Class<?> clazz, Set<Class<?>> visitedClasses, Set<Class<?>> validatedClasses,
118+
Set<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
119+
120+
Assert.notNull(validator, "Validator cannot be null");
120121

121-
Assert.notNull(validator, "Validator can't be null");
122+
if (!visitedClasses.add(clazz)) {
123+
return;
124+
}
122125

123126
BeanDescriptor descriptor;
124127
try {
@@ -149,12 +152,12 @@ else if (ex instanceof TypeNotPresentException) {
149152

150153
ReflectionUtils.doWithFields(clazz, field -> {
151154
Class<?> type = field.getType();
152-
if (Iterable.class.isAssignableFrom(type) || List.class.isAssignableFrom(type) || Optional.class.isAssignableFrom(type)) {
155+
if (Iterable.class.isAssignableFrom(type) || Optional.class.isAssignableFrom(type)) {
153156
ResolvableType resolvableType = ResolvableType.forField(field);
154157
Class<?> genericType = resolvableType.getGeneric(0).toClass();
155158
if (shouldProcess(genericType)) {
156159
validatedClasses.add(clazz);
157-
processAheadOfTime(genericType, validatedClasses, constraintValidatorClasses);
160+
processAheadOfTime(genericType, visitedClasses, validatedClasses, constraintValidatorClasses);
158161
}
159162
}
160163
if (Map.class.isAssignableFrom(type)) {
@@ -163,11 +166,11 @@ else if (ex instanceof TypeNotPresentException) {
163166
Class<?> valueGenericType = resolvableType.getGeneric(1).toClass();
164167
if (shouldProcess(keyGenericType)) {
165168
validatedClasses.add(clazz);
166-
processAheadOfTime(keyGenericType, validatedClasses, constraintValidatorClasses);
169+
processAheadOfTime(keyGenericType, visitedClasses, validatedClasses, constraintValidatorClasses);
167170
}
168171
if (shouldProcess(valueGenericType)) {
169172
validatedClasses.add(clazz);
170-
processAheadOfTime(valueGenericType, validatedClasses, constraintValidatorClasses);
173+
processAheadOfTime(valueGenericType, visitedClasses, validatedClasses, constraintValidatorClasses);
171174
}
172175
}
173176
});

spring-context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessorTests.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -22,6 +22,8 @@
2222
import java.lang.annotation.Target;
2323
import java.util.ArrayList;
2424
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Optional;
2527

2628
import jakarta.validation.Constraint;
2729
import jakarta.validation.ConstraintValidator;
@@ -31,6 +33,8 @@
3133
import jakarta.validation.constraints.Pattern;
3234
import org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator;
3335
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.params.ParameterizedTest;
37+
import org.junit.jupiter.params.provider.ValueSource;
3438

3539
import org.springframework.aot.generate.GenerationContext;
3640
import org.springframework.aot.hint.MemberCategory;
@@ -121,6 +125,15 @@ void shouldProcessTransitiveGenericTypeLevelConstraint() {
121125
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.generationContext.getRuntimeHints());
122126
}
123127

128+
@ParameterizedTest // gh-33936
129+
@ValueSource(classes = {BeanWithRecursiveIterable.class, BeanWithRecursiveMap.class, BeanWithRecursiveOptional.class})
130+
void shouldProcessRecursiveGenericsWithoutInfiniteRecursion(Class<?> beanClass) {
131+
process(beanClass);
132+
assertThat(this.generationContext.getRuntimeHints().reflection().typeHints()).hasSize(1);
133+
assertThat(RuntimeHintsPredicates.reflection().onType(beanClass)
134+
.withMemberCategory(MemberCategory.DECLARED_FIELDS)).accepts(this.generationContext.getRuntimeHints());
135+
}
136+
124137
private void process(Class<?> beanClass) {
125138
BeanRegistrationAotContribution contribution = createContribution(beanClass);
126139
if (contribution != null) {
@@ -244,4 +257,16 @@ public void setExclude(List<Exclude> exclude) {
244257
}
245258
}
246259

260+
static class BeanWithRecursiveIterable {
261+
Iterable<BeanWithRecursiveIterable> iterable;
262+
}
263+
264+
static class BeanWithRecursiveMap {
265+
Map<BeanWithRecursiveMap, BeanWithRecursiveMap> map;
266+
}
267+
268+
static class BeanWithRecursiveOptional {
269+
Optional<BeanWithRecursiveOptional> optional;
270+
}
271+
247272
}

spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,10 @@ public String getContentAsString(Charset fallbackCharset) throws UnsupportedEnco
319319

320320
@Override
321321
public void setContentLength(int contentLength) {
322-
this.contentLength = contentLength;
323-
doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true);
322+
if (!this.committed) {
323+
this.contentLength = contentLength;
324+
doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true);
325+
}
324326
}
325327

326328
/**
@@ -334,8 +336,10 @@ public int getContentLength() {
334336

335337
@Override
336338
public void setContentLengthLong(long contentLength) {
337-
this.contentLength = contentLength;
338-
doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true);
339+
if (!this.committed) {
340+
this.contentLength = contentLength;
341+
doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true);
342+
}
339343
}
340344

341345
public long getContentLengthLong() {

spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
* @author Vedran Pavic
4646
* @since 1.0.2
4747
*/
48-
@SuppressWarnings("deprecation")
4948
public class MockHttpSession implements HttpSession {
5049

5150
/**
@@ -240,6 +239,12 @@ public boolean isNew() {
240239
return this.isNew;
241240
}
242241

242+
@Override
243+
public Accessor getAccessor() {
244+
return sessionConsumer -> sessionConsumer.accept(MockHttpSession.this);
245+
}
246+
247+
243248
/**
244249
* Serialize the attributes of this session into an object that can be
245250
* turned into a byte array with standard Java serialization.

spring-web/src/main/java/org/springframework/http/server/reactive/JettyHttpHandlerAdapter.java

Lines changed: 0 additions & 83 deletions
This file was deleted.

spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.net.InetSocketAddress;
2121
import java.net.URI;
2222
import java.net.URISyntaxException;
23+
import java.nio.ByteBuffer;
2324
import java.nio.charset.Charset;
2425
import java.security.cert.X509Certificate;
2526
import java.util.Enumeration;
@@ -38,6 +39,7 @@
3839

3940
import org.springframework.core.io.buffer.DataBuffer;
4041
import org.springframework.core.io.buffer.DataBufferFactory;
42+
import org.springframework.core.io.buffer.DataBufferUtils;
4143
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
4244
import org.springframework.http.HttpCookie;
4345
import org.springframework.http.HttpHeaders;
@@ -58,6 +60,7 @@
5860
*
5961
* @author Rossen Stoyanchev
6062
* @author Juergen Hoeller
63+
* @author Brian Clozel
6164
* @since 5.0
6265
*/
6366
class ServletServerHttpRequest extends AbstractServerHttpRequest {
@@ -75,7 +78,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
7578

7679
private final DataBufferFactory bufferFactory;
7780

78-
private final byte[] buffer;
81+
private final int bufferSize;
7982

8083
private final AsyncListener asyncListener;
8184

@@ -99,7 +102,7 @@ public ServletServerHttpRequest(MultiValueMap<String, String> headers, HttpServl
99102

100103
this.request = request;
101104
this.bufferFactory = bufferFactory;
102-
this.buffer = new byte[bufferSize];
105+
this.bufferSize = bufferSize;
103106

104107
this.asyncListener = new RequestAsyncListener();
105108

@@ -275,20 +278,31 @@ protected final ServletInputStream getInputStream() {
275278
* or {@link #EOF_BUFFER} if the input stream returned -1.
276279
*/
277280
DataBuffer readFromInputStream() throws IOException {
278-
int read = this.inputStream.read(this.buffer);
279-
logBytesRead(read);
280-
281-
if (read > 0) {
282-
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(read);
283-
dataBuffer.write(this.buffer, 0, read);
284-
return dataBuffer;
281+
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(this.bufferSize);
282+
int read = -1;
283+
try {
284+
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.writableByteBuffers()) {
285+
Assert.state(iterator.hasNext(), "No ByteBuffer available");
286+
ByteBuffer byteBuffer = iterator.next();
287+
read = this.inputStream.read(byteBuffer);
288+
}
289+
logBytesRead(read);
290+
if (read > 0) {
291+
dataBuffer.writePosition(read);
292+
return dataBuffer;
293+
}
294+
else if (read == -1) {
295+
return EOF_BUFFER;
296+
}
297+
else {
298+
return AbstractListenerReadPublisher.EMPTY_BUFFER;
299+
}
285300
}
286-
287-
if (read == -1) {
288-
return EOF_BUFFER;
301+
finally {
302+
if (read <= 0) {
303+
DataBufferUtils.release(dataBuffer);
304+
}
289305
}
290-
291-
return AbstractListenerReadPublisher.EMPTY_BUFFER;
292306
}
293307

294308
protected final void logBytesRead(int read) {

spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpResponse.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.springframework.http.server.reactive;
1818

1919
import java.io.IOException;
20-
import java.io.InputStream;
20+
import java.nio.ByteBuffer;
2121
import java.nio.charset.Charset;
2222

2323
import jakarta.servlet.AsyncContext;
@@ -45,6 +45,7 @@
4545
*
4646
* @author Rossen Stoyanchev
4747
* @author Juergen Hoeller
48+
* @author Brian Clozel
4849
* @since 5.0
4950
*/
5051
class ServletServerHttpResponse extends AbstractListenerServerHttpResponse {
@@ -222,15 +223,15 @@ protected final ServletOutputStream getOutputStream() {
222223
*/
223224
protected int writeToOutputStream(DataBuffer dataBuffer) throws IOException {
224225
ServletOutputStream outputStream = this.outputStream;
225-
InputStream input = dataBuffer.asInputStream();
226-
int bytesWritten = 0;
227-
byte[] buffer = new byte[this.bufferSize];
228-
int bytesRead;
229-
while (outputStream.isReady() && (bytesRead = input.read(buffer)) != -1) {
230-
outputStream.write(buffer, 0, bytesRead);
231-
bytesWritten += bytesRead;
226+
int len = 0;
227+
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
228+
while (iterator.hasNext() && outputStream.isReady()) {
229+
ByteBuffer byteBuffer = iterator.next();
230+
len += byteBuffer.remaining();
231+
outputStream.write(byteBuffer);
232+
}
232233
}
233-
return bytesWritten;
234+
return len;
234235
}
235236

236237
private void flush() throws IOException {

0 commit comments

Comments
 (0)