Refactor JettyDataBuffer with new JettyVirtualDataBuffer

This commits simplifies the Jetty buffer support by consolidating
the shared logic and delegating operations to the parent class
thanks to the new `JettyVirtualDataBuffer`.

Signed-off-by: Istvan Verhas <vi@mocker.guru>
This commit is contained in:
Istvan Verhas
2026-08-28 14:42:41 +02:00
committed by Brian Clozel
parent 17e0daf8a1
commit 752193fca9
3 changed files with 299 additions and 260 deletions
@@ -17,303 +17,73 @@
package org.springframework.core.io.buffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntPredicate;
import org.eclipse.jetty.io.Content;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
* Implementation of the {@code DataBuffer} interface that can wrap a Jetty
* {@link Content.Chunk}. Typically constructed with {@link JettyDataBufferFactory}.
* Implementation of the {@code DataBuffer} interface that can wrap a Jetty {@link Content.Chunk}. Typically constructed
* with {@link JettyDataBufferFactory}.
*
* @author Greg Wilkins
* @author Lachlan Roberts
* @author Arjen Poutsma
* @since 6.2
*/
public final class JettyDataBuffer implements PooledDataBuffer {
public final class JettyDataBuffer extends JettyVirtualDataBuffer {
private final DefaultDataBuffer delegate;
private final Content.Chunk chunk;
private final Content.@Nullable Chunk chunk;
JettyDataBuffer(JettyDataBufferFactory bufferFactory, DataBuffer delegate, Content.Chunk chunk) {
super(bufferFactory, delegate);
private final JettyDataBufferFactory bufferFactory;
private final AtomicInteger refCount = new AtomicInteger(1);
JettyDataBuffer(JettyDataBufferFactory bufferFactory, DefaultDataBuffer delegate, Content.Chunk chunk) {
Assert.notNull(bufferFactory, "BufferFactory must not be null");
Assert.notNull(delegate, "Delegate must not be null");
Assert.notNull(chunk, "Chunk must not be null");
this.bufferFactory = bufferFactory;
this.delegate = delegate;
this.chunk = chunk;
}
JettyDataBuffer(JettyDataBufferFactory bufferFactory, DefaultDataBuffer delegate) {
Assert.notNull(bufferFactory, "BufferFactory must not be null");
Assert.notNull(delegate, "Delegate must not be null");
this.bufferFactory = bufferFactory;
this.delegate = delegate;
this.chunk = null;
}
@Override
public boolean isAllocated() {
return this.refCount.get() > 0;
}
@Override
public PooledDataBuffer retain() {
int result = this.refCount.updateAndGet(c -> (c != 0 ? c + 1 : 0));
if (result != 0 && this.chunk != null) {
int result = incrementRefCount();
if (result != 0) {
this.chunk.retain();
}
return this;
}
@Override
public PooledDataBuffer touch(Object hint) {
return this;
}
@Override
public boolean release() {
int result = this.refCount.updateAndGet(c -> {
if (c != 0) {
return c - 1;
}
else {
throw new IllegalStateException("JettyDataBuffer already released: " + this);
}
});
if (this.chunk != null) {
return this.chunk.release();
}
else {
return (result == 0);
}
}
@Override
public DataBufferFactory factory() {
return this.bufferFactory;
}
// delegation
@Override
public int indexOf(IntPredicate predicate, int fromIndex) {
return this.delegate.indexOf(predicate, fromIndex);
}
@Override
public int lastIndexOf(IntPredicate predicate, int fromIndex) {
return this.delegate.lastIndexOf(predicate, fromIndex);
}
@Override
public int readableByteCount() {
return this.delegate.readableByteCount();
}
@Override
public int writableByteCount() {
return this.delegate.writableByteCount();
}
@Override
public int capacity() {
return this.delegate.capacity();
}
@Override
@Deprecated(since = "6.0")
public DataBuffer capacity(int capacity) {
this.delegate.capacity(capacity);
return this;
}
@Override
public DataBuffer ensureWritable(int capacity) {
this.delegate.ensureWritable(capacity);
return this;
}
@Override
public int readPosition() {
return this.delegate.readPosition();
}
@Override
public DataBuffer readPosition(int readPosition) {
this.delegate.readPosition(readPosition);
return this;
}
@Override
public int writePosition() {
return this.delegate.writePosition();
}
@Override
public DataBuffer writePosition(int writePosition) {
this.delegate.writePosition(writePosition);
return this;
}
@Override
public byte getByte(int index) {
return this.delegate.getByte(index);
}
@Override
public byte read() {
return this.delegate.read();
}
@Override
public DataBuffer read(byte[] destination) {
this.delegate.read(destination);
return this;
}
@Override
public DataBuffer read(byte[] destination, int offset, int length) {
this.delegate.read(destination, offset, length);
return this;
}
@Override
public DataBuffer write(byte b) {
this.delegate.write(b);
return this;
}
@Override
public DataBuffer write(byte[] source) {
this.delegate.write(source);
return this;
}
@Override
public DataBuffer write(byte[] source, int offset, int length) {
this.delegate.write(source, offset, length);
return this;
}
@Override
public DataBuffer write(DataBuffer... buffers) {
this.delegate.write(buffers);
return this;
}
@Override
public DataBuffer write(ByteBuffer... buffers) {
this.delegate.write(buffers);
return this;
decrementRefCount();
return this.chunk.release();
}
@Override
@Deprecated(since = "6.0")
public DataBuffer slice(int index, int length) {
DefaultDataBuffer delegateSlice = this.delegate.slice(index, length);
if (this.chunk != null) {
this.chunk.retain();
return new JettyDataBuffer(this.bufferFactory, delegateSlice, this.chunk);
}
else {
return new JettyDataBuffer(this.bufferFactory, delegateSlice);
}
DataBuffer delegateSlice = super.slice(index, length);
this.chunk.retain();
return new JettyDataBuffer(this.bufferFactory, delegateSlice, this.chunk);
}
@Override
public DataBuffer split(int index) {
DefaultDataBuffer delegateSplit = this.delegate.split(index);
if (this.chunk != null) {
this.chunk.retain();
return new JettyDataBuffer(this.bufferFactory, delegateSplit, this.chunk);
}
else {
return new JettyDataBuffer(this.bufferFactory, delegateSplit);
}
DataBuffer delegateSplit = super.split(index);
this.chunk.retain();
return new JettyDataBuffer(this.bufferFactory, delegateSplit, this.chunk);
}
@Override
@Deprecated(since = "6.0")
public ByteBuffer asByteBuffer() {
return this.delegate.asByteBuffer();
}
@Override
@Deprecated(since = "6.0")
public ByteBuffer asByteBuffer(int index, int length) {
return this.delegate.asByteBuffer(index, length);
}
@Override
@Deprecated(since = "6.0.5")
public ByteBuffer toByteBuffer(int index, int length) {
return this.delegate.toByteBuffer(index, length);
}
@Override
public void toByteBuffer(int srcPos, ByteBuffer dest, int destPos, int length) {
this.delegate.toByteBuffer(srcPos, dest, destPos, length);
}
@Override
public ByteBufferIterator readableByteBuffers() {
ByteBufferIterator delegateIterator = this.delegate.readableByteBuffers();
if (this.chunk != null) {
return new JettyByteBufferIterator(delegateIterator, this.chunk);
}
else {
return delegateIterator;
}
ByteBufferIterator delegateIterator = super.readableByteBuffers();
return new JettyByteBufferIterator(delegateIterator, this.chunk);
}
@Override
public ByteBufferIterator writableByteBuffers() {
ByteBufferIterator delegateIterator = this.delegate.writableByteBuffers();
if (this.chunk != null) {
return new JettyByteBufferIterator(delegateIterator, this.chunk);
}
else {
return delegateIterator;
}
}
@Override
public String toString(int index, int length, Charset charset) {
return this.delegate.toString(index, length, charset);
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof JettyDataBuffer otherBuffer &&
this.delegate.equals(otherBuffer.delegate)));
}
@Override
public int hashCode() {
return this.delegate.hashCode();
}
@Override
public String toString() {
return String.format("JettyDataBuffer (r: %d, w: %d, c: %d)",
readPosition(), writePosition(), capacity());
ByteBufferIterator delegateIterator = super.writableByteBuffers();
return new JettyByteBufferIterator(delegateIterator, this.chunk);
}
@@ -66,27 +66,27 @@ public class JettyDataBufferFactory implements DataBufferFactory {
@Override
@Deprecated(since = "6.0")
public JettyDataBuffer allocateBuffer() {
public DataBuffer allocateBuffer() {
DefaultDataBuffer delegate = this.delegate.allocateBuffer();
return new JettyDataBuffer(this, delegate);
return new JettyVirtualDataBuffer(this, delegate);
}
@Override
public JettyDataBuffer allocateBuffer(int initialCapacity) {
public DataBuffer allocateBuffer(int initialCapacity) {
DefaultDataBuffer delegate = this.delegate.allocateBuffer(initialCapacity);
return new JettyDataBuffer(this, delegate);
return new JettyVirtualDataBuffer(this, delegate);
}
@Override
public JettyDataBuffer wrap(ByteBuffer byteBuffer) {
public DataBuffer wrap(ByteBuffer byteBuffer) {
DefaultDataBuffer delegate = this.delegate.wrap(byteBuffer);
return new JettyDataBuffer(this, delegate);
return new JettyVirtualDataBuffer(this, delegate);
}
@Override
public JettyDataBuffer wrap(byte[] bytes) {
public DataBuffer wrap(byte[] bytes) {
DefaultDataBuffer delegate = this.delegate.wrap(bytes);
return new JettyDataBuffer(this, delegate);
return new JettyVirtualDataBuffer(this, delegate);
}
public JettyDataBuffer wrap(Content.Chunk chunk) {
@@ -96,9 +96,9 @@ public class JettyDataBufferFactory implements DataBufferFactory {
}
@Override
public JettyDataBuffer join(List<? extends DataBuffer> dataBuffers) {
public DataBuffer join(List<? extends DataBuffer> dataBuffers) {
DefaultDataBuffer delegate = this.delegate.join(dataBuffers);
return new JettyDataBuffer(this, delegate);
return new JettyVirtualDataBuffer(this, delegate);
}
@Override
@@ -0,0 +1,269 @@
/*
* Copyright 2002-present the original author or authors.
*
* 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
*
* https://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.springframework.core.io.buffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntPredicate;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
* Implementation of the {@code DataBuffer} interface that can wrap another {@link DataBuffer}.
* Typically constructed with {@link JettyDataBufferFactory}.
* @author Istvan Verhas
*/
class JettyVirtualDataBuffer implements PooledDataBuffer {
final JettyDataBufferFactory bufferFactory;
private final DataBuffer delegate;
private final AtomicInteger refCount = new AtomicInteger(1);
JettyVirtualDataBuffer(JettyDataBufferFactory bufferFactory, DataBuffer delegate) {
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
this.bufferFactory = bufferFactory;
}
// delegation
@Override
public boolean isAllocated() {
return this.refCount.get() > 0;
}
@Override
public PooledDataBuffer retain() {
incrementRefCount();
return this;
}
int incrementRefCount() {
return this.refCount.updateAndGet(c -> (c != 0 ? c + 1 : 0));
}
@Override
public PooledDataBuffer touch(Object hint) {
return this;
}
@Override
public boolean release() {
int result = decrementRefCount();
return (result == 0);
}
int decrementRefCount() {
return this.refCount.updateAndGet(c -> {
if (c != 0) {
return c - 1;
}
else {
throw new IllegalStateException("JettyDataBuffer already released: " + this);
}
});
}
@Override
public DataBufferFactory factory() {
return this.bufferFactory;
}
@Override
public int indexOf(IntPredicate predicate, int fromIndex) {
return this.delegate.indexOf(predicate, fromIndex);
}
@Override
public int lastIndexOf(IntPredicate predicate, int fromIndex) {
return this.delegate.lastIndexOf(predicate, fromIndex);
}
@Override
public int readableByteCount() {
return this.delegate.readableByteCount();
}
@Override
public int writableByteCount() {
return this.delegate.writableByteCount();
}
@Override
public int capacity() {
return this.delegate.capacity();
}
@Override
@Deprecated(since = "6.0")
public DataBuffer capacity(int capacity) {
this.delegate.capacity(capacity);
return this;
}
@Override
public DataBuffer ensureWritable(int capacity) {
this.delegate.ensureWritable(capacity);
return this;
}
@Override
public int readPosition() {
return this.delegate.readPosition();
}
@Override
public DataBuffer readPosition(int readPosition) {
this.delegate.readPosition(readPosition);
return this;
}
@Override
public int writePosition() {
return this.delegate.writePosition();
}
@Override
public DataBuffer writePosition(int writePosition) {
this.delegate.writePosition(writePosition);
return this;
}
@Override
public byte getByte(int index) {
return this.delegate.getByte(index);
}
@Override
public byte read() {
return this.delegate.read();
}
@Override
public DataBuffer read(byte[] destination) {
this.delegate.read(destination);
return this;
}
@Override
public DataBuffer read(byte[] destination, int offset, int length) {
this.delegate.read(destination, offset, length);
return this;
}
@Override
public DataBuffer write(byte b) {
this.delegate.write(b);
return this;
}
@Override
public DataBuffer write(byte[] source) {
this.delegate.write(source);
return this;
}
@Override
public DataBuffer write(byte[] source, int offset, int length) {
this.delegate.write(source, offset, length);
return this;
}
@Override
public DataBuffer write(DataBuffer... buffers) {
this.delegate.write(buffers);
return this;
}
@Override
public DataBuffer write(ByteBuffer... buffers) {
this.delegate.write(buffers);
return this;
}
@Override
@Deprecated(since = "6.0")
public DataBuffer slice(int index, int length) {
DataBuffer delegateSlice = this.delegate.slice(index, length);
return new JettyVirtualDataBuffer(this.bufferFactory, delegateSlice);
}
@Override
public DataBuffer split(int index) {
DataBuffer delegateSplit = this.delegate.split(index);
return new JettyVirtualDataBuffer(this.bufferFactory, delegateSplit);
}
@Override
@Deprecated(since = "6.0")
public ByteBuffer asByteBuffer() {
return this.delegate.asByteBuffer();
}
@Override
@Deprecated(since = "6.0")
public ByteBuffer asByteBuffer(int index, int length) {
return this.delegate.asByteBuffer(index, length);
}
@Override
@Deprecated(since = "6.0.5")
public ByteBuffer toByteBuffer(int index, int length) {
return this.delegate.toByteBuffer(index, length);
}
@Override
public void toByteBuffer(int srcPos, ByteBuffer dest, int destPos, int length) {
this.delegate.toByteBuffer(srcPos, dest, destPos, length);
}
@Override
public ByteBufferIterator readableByteBuffers() {
return this.delegate.readableByteBuffers();
}
@Override
public ByteBufferIterator writableByteBuffers() {
return this.delegate.writableByteBuffers();
}
@Override
public String toString(int index, int length, Charset charset) {
return this.delegate.toString(index, length, charset);
}
@Override
public int hashCode() {
return this.delegate.hashCode();
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof JettyVirtualDataBuffer otherBuffer &&
this.delegate.equals(otherBuffer.delegate)));
}
@Override
public String toString() {
return String.format("JettyDataBuffer (r: %d, w: %d, c: %d)",
readPosition(), writePosition(), capacity());
}
}