Skip to content

Commit 9c33d27

Browse files
committed
Introduce support for Netty 5 Buffer
This commit introduces support for Netty 5's Buffer, in the form of Netty5DataBuffer. Because of the new API offered by Buffer, several changes have been made to the DataBuffer API: - CloseableDataBuffer is a simpler alternative to PooledDataBuffer, and implemented by Netty5DataBuffer. DataBufferUtils::release can now handle CloseableDataBuffer as well as PooledDataBuffer. - PooledDataBuffer::touch has been moved into a separate interface: TouchableDataBuffer, which is implemented by Netty5DataBuffer. - The capacity of DataBuffers can no longer be reduced, they can only grow larger. As a consequence, DataBuffer::capacity(int) has been deprecated, but ensureWritable (formally ensureCapacity) still exists. - DataBuffer::slice and retainedSlice have been deprecated in favor of split, a new method that ensures that memory regions do not overlap. - DataBuffer::asByteBuffer has been deprecated in favor of toByteBuffer, a new method that returns a copy, instead of shared data. - DataBufferFactory::allocateBuffer has been deprecated in favor of allocateBuffer(int). Closes gh-28874
1 parent 8a61866 commit 9c33d27

File tree

64 files changed

+1663
-360
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1663
-360
lines changed

spring-core/spring-core.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ dependencies {
7272
optional("io.reactivex.rxjava3:rxjava")
7373
optional("io.smallrye.reactive:mutiny")
7474
optional("io.netty:netty-buffer")
75+
optional("io.netty:netty5-buffer:5.0.0.Alpha4")
7576
testImplementation("jakarta.annotation:jakarta.annotation-api")
7677
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api")
7778
testImplementation("com.google.code.findbugs:jsr305")

spring-core/src/main/java/org/springframework/core/codec/AbstractSingleValueEncoder.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -25,7 +25,6 @@
2525
import org.springframework.core.io.buffer.DataBuffer;
2626
import org.springframework.core.io.buffer.DataBufferFactory;
2727
import org.springframework.core.io.buffer.DataBufferUtils;
28-
import org.springframework.core.io.buffer.PooledDataBuffer;
2928
import org.springframework.lang.Nullable;
3029
import org.springframework.util.MimeType;
3130

@@ -52,7 +51,7 @@ public final Flux<DataBuffer> encode(Publisher<? extends T> inputStream, DataBuf
5251
return Flux.from(inputStream)
5352
.take(1)
5453
.concatMap(value -> encode(value, bufferFactory, elementType, mimeType, hints))
55-
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
54+
.doOnDiscard(DataBuffer.class, DataBufferUtils::release);
5655
}
5756

5857
/**

spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -51,15 +51,12 @@ public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType
5151
public ByteBuffer decode(DataBuffer dataBuffer, ResolvableType elementType,
5252
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
5353

54-
int byteCount = dataBuffer.readableByteCount();
55-
ByteBuffer copy = ByteBuffer.allocate(byteCount);
56-
copy.put(dataBuffer.asByteBuffer());
57-
copy.flip();
58-
DataBufferUtils.release(dataBuffer);
54+
ByteBuffer result = dataBuffer.toByteBuffer();
5955
if (logger.isDebugEnabled()) {
60-
logger.debug(Hints.getLogPrefix(hints) + "Read " + byteCount + " bytes");
56+
logger.debug(Hints.getLogPrefix(hints) + "Read " + dataBuffer.readableByteCount() + " bytes");
6157
}
62-
return copy;
58+
DataBufferUtils.release(dataBuffer);
59+
return result;
6360
}
6461

6562
}

spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -35,7 +35,7 @@
3535
* after they have been consumed. In addition, if using {@code Flux} or
3636
* {@code Mono} operators such as flatMap, reduce, and others that prefetch,
3737
* cache, and skip or filter out data items internally, please add
38-
* {@code doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release)} to the
38+
* {@code doOnDiscard(DataBuffer.class, DataBufferUtils::release)} to the
3939
* composition chain to ensure cached data buffers are released prior to an
4040
* error or cancellation signal.
4141
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.codec;
18+
19+
import java.util.Map;
20+
21+
import io.netty5.buffer.api.Buffer;
22+
import io.netty5.buffer.api.DefaultBufferAllocators;
23+
24+
import org.springframework.core.ResolvableType;
25+
import org.springframework.core.io.buffer.DataBuffer;
26+
import org.springframework.core.io.buffer.DataBufferUtils;
27+
import org.springframework.core.io.buffer.Netty5DataBuffer;
28+
import org.springframework.lang.Nullable;
29+
import org.springframework.util.MimeType;
30+
import org.springframework.util.MimeTypeUtils;
31+
32+
/**
33+
* Decoder for {@link Buffer Buffers}.
34+
*
35+
* @author Violeta Georgieva
36+
* @since 6.0
37+
*/
38+
public class Netty5BufferDecoder extends AbstractDataBufferDecoder<Buffer> {
39+
40+
public Netty5BufferDecoder() {
41+
super(MimeTypeUtils.ALL);
42+
}
43+
44+
45+
@Override
46+
public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
47+
return (Buffer.class.isAssignableFrom(elementType.toClass()) &&
48+
super.canDecode(elementType, mimeType));
49+
}
50+
51+
@Override
52+
public Buffer decode(DataBuffer dataBuffer, ResolvableType elementType,
53+
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
54+
55+
if (logger.isDebugEnabled()) {
56+
logger.debug(Hints.getLogPrefix(hints) + "Read " + dataBuffer.readableByteCount() + " bytes");
57+
}
58+
if (dataBuffer instanceof Netty5DataBuffer netty5DataBuffer) {
59+
return netty5DataBuffer.getNativeBuffer();
60+
}
61+
byte[] bytes = new byte[dataBuffer.readableByteCount()];
62+
dataBuffer.read(bytes);
63+
Buffer buffer = DefaultBufferAllocators.preferredAllocator().copyOf(bytes);
64+
DataBufferUtils.release(dataBuffer);
65+
return buffer;
66+
}
67+
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.codec;
18+
19+
import java.util.Map;
20+
21+
import io.netty5.buffer.api.Buffer;
22+
import org.reactivestreams.Publisher;
23+
import reactor.core.publisher.Flux;
24+
25+
import org.springframework.core.ResolvableType;
26+
import org.springframework.core.io.buffer.DataBuffer;
27+
import org.springframework.core.io.buffer.DataBufferFactory;
28+
import org.springframework.core.io.buffer.Netty5DataBufferFactory;
29+
import org.springframework.lang.Nullable;
30+
import org.springframework.util.MimeType;
31+
import org.springframework.util.MimeTypeUtils;
32+
33+
/**
34+
* Encoder for {@link Buffer Buffers}.
35+
*
36+
* @author Violeta Georgieva
37+
* @since 6.0
38+
*/
39+
public class Netty5BufferEncoder extends AbstractEncoder<Buffer> {
40+
41+
public Netty5BufferEncoder() {
42+
super(MimeTypeUtils.ALL);
43+
}
44+
45+
46+
@Override
47+
public boolean canEncode(ResolvableType type, @Nullable MimeType mimeType) {
48+
Class<?> clazz = type.toClass();
49+
return super.canEncode(type, mimeType) && Buffer.class.isAssignableFrom(clazz);
50+
}
51+
52+
@Override
53+
public Flux<DataBuffer> encode(Publisher<? extends Buffer> inputStream,
54+
DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,
55+
@Nullable Map<String, Object> hints) {
56+
57+
return Flux.from(inputStream).map(byteBuffer ->
58+
encodeValue(byteBuffer, bufferFactory, elementType, mimeType, hints));
59+
}
60+
61+
@Override
62+
public DataBuffer encodeValue(Buffer buffer, DataBufferFactory bufferFactory, ResolvableType valueType,
63+
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
64+
65+
if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) {
66+
String logPrefix = Hints.getLogPrefix(hints);
67+
logger.debug(logPrefix + "Writing " + buffer.readableBytes() + " bytes");
68+
}
69+
if (bufferFactory instanceof Netty5DataBufferFactory netty5DataBufferFactory) {
70+
return netty5DataBufferFactory.wrap(buffer);
71+
}
72+
byte[] bytes = new byte[buffer.readableBytes()];
73+
buffer.readBytes(bytes, 0, bytes.length);
74+
buffer.close();
75+
return bufferFactory.wrap(bytes);
76+
}
77+
}

spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java

+11-12
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import org.springframework.core.io.buffer.DataBuffer;
3636
import org.springframework.core.io.buffer.DataBufferUtils;
3737
import org.springframework.core.io.buffer.LimitedDataBufferList;
38-
import org.springframework.core.io.buffer.PooledDataBuffer;
3938
import org.springframework.core.log.LogFormatUtils;
4039
import org.springframework.lang.Nullable;
4140
import org.springframework.util.Assert;
@@ -127,7 +126,7 @@ public Flux<String> decode(Publisher<DataBuffer> input, ResolvableType elementTy
127126
return Mono.just(lastBuffer);
128127
}))
129128
.doOnTerminate(chunks::releaseAndClear)
130-
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)
129+
.doOnDiscard(DataBuffer.class, DataBufferUtils::release)
131130
.map(buffer -> decode(buffer, elementType, mimeType, hints));
132131
}
133132

@@ -153,26 +152,26 @@ private Collection<DataBuffer> processDataBuffer(
153152
DataBufferUtils.retain(buffer); // retain after add (may raise DataBufferLimitException)
154153
break;
155154
}
156-
int startIndex = buffer.readPosition();
157-
int length = (endIndex - startIndex + 1);
158-
DataBuffer slice = buffer.retainedSlice(startIndex, length);
159-
result = (result != null ? result : new ArrayList<>());
155+
DataBuffer split = buffer.split(endIndex + 1);
156+
if (result == null) {
157+
result = new ArrayList<>();
158+
}
159+
int delimiterLength = matcher.delimiter().length;
160160
if (chunks.isEmpty()) {
161161
if (this.stripDelimiter) {
162-
slice.writePosition(slice.writePosition() - matcher.delimiter().length);
162+
split.writePosition(split.writePosition() - delimiterLength);
163163
}
164-
result.add(slice);
164+
result.add(split);
165165
}
166166
else {
167-
chunks.add(slice);
167+
chunks.add(split);
168168
DataBuffer joined = buffer.factory().join(chunks);
169169
if (this.stripDelimiter) {
170-
joined.writePosition(joined.writePosition() - matcher.delimiter().length);
170+
joined.writePosition(joined.writePosition() - delimiterLength);
171171
}
172172
result.add(joined);
173173
chunks.clear();
174174
}
175-
buffer.readPosition(endIndex + 1);
176175
}
177176
while (buffer.readableByteCount() > 0);
178177
return (result != null ? result : Collections.emptyList());
@@ -187,7 +186,7 @@ public String decode(DataBuffer dataBuffer, ResolvableType elementType,
187186
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
188187

189188
Charset charset = getCharset(mimeType);
190-
CharBuffer charBuffer = charset.decode(dataBuffer.asByteBuffer());
189+
CharBuffer charBuffer = charset.decode(dataBuffer.toByteBuffer());
191190
DataBufferUtils.release(dataBuffer);
192191
String value = charBuffer.toString();
193192
LogFormatUtils.traceDebug(logger, traceOn -> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.io.buffer;
18+
19+
/**
20+
* Extension of {@link DataBuffer} that allows for buffers that can be used
21+
* in a {@code try}-with-resources statement.
22+
23+
* @author Arjen Poutsma
24+
* @since 6.0
25+
*/
26+
public interface CloseableDataBuffer extends DataBuffer, AutoCloseable {
27+
28+
/**
29+
* Closes this data buffer, freeing any resources.
30+
* @throws IllegalStateException if this buffer has already been closed
31+
*/
32+
@Override
33+
void close();
34+
35+
}

0 commit comments

Comments
 (0)