Explicit calls on Buffer base type (for compatibility with covariant return type on JDK 9's ByteBuffer)
Issue: SPR-14645
This commit is contained in:
parent
2fea1f7da3
commit
ffc781b163
|
|
@ -19,6 +19,7 @@ package org.springframework.core.io.buffer;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
|
@ -149,7 +150,8 @@ public class DefaultDataBuffer implements DataBuffer {
|
|||
* applying the given function on {@link #byteBuffer}.
|
||||
*/
|
||||
private <T> T readInternal(Function<ByteBuffer, T> function) {
|
||||
this.byteBuffer.position(this.readPosition);
|
||||
// Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
|
||||
((Buffer) this.byteBuffer).position(this.readPosition);
|
||||
try {
|
||||
return function.apply(this.byteBuffer);
|
||||
}
|
||||
|
|
@ -207,7 +209,8 @@ public class DefaultDataBuffer implements DataBuffer {
|
|||
* after applying the given function on {@link #byteBuffer}.
|
||||
*/
|
||||
private <T> T writeInternal(Function<ByteBuffer, T> function) {
|
||||
this.byteBuffer.position(this.writePosition);
|
||||
// Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
|
||||
((Buffer) this.byteBuffer).position(this.writePosition);
|
||||
try {
|
||||
return function.apply(this.byteBuffer);
|
||||
}
|
||||
|
|
@ -219,23 +222,29 @@ public class DefaultDataBuffer implements DataBuffer {
|
|||
@Override
|
||||
public DataBuffer slice(int index, int length) {
|
||||
int oldPosition = this.byteBuffer.position();
|
||||
// Explicit access via Buffer base type for compatibility
|
||||
// with covariant return type on JDK 9's ByteBuffer...
|
||||
Buffer buffer = this.byteBuffer;
|
||||
try {
|
||||
this.byteBuffer.position(index);
|
||||
buffer.position(index);
|
||||
ByteBuffer slice = this.byteBuffer.slice();
|
||||
slice.limit(length);
|
||||
// Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
|
||||
((Buffer) slice).limit(length);
|
||||
return new SlicedDefaultDataBuffer(slice, 0, length, this.dataBufferFactory);
|
||||
}
|
||||
finally {
|
||||
this.byteBuffer.position(oldPosition);
|
||||
buffer.position(oldPosition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asByteBuffer() {
|
||||
ByteBuffer duplicate = this.byteBuffer.duplicate();
|
||||
duplicate.position(this.readPosition);
|
||||
duplicate.limit(this.writePosition);
|
||||
// Explicit access via Buffer base type for compatibility
|
||||
// with covariant return type on JDK 9's ByteBuffer...
|
||||
Buffer buffer = duplicate;
|
||||
buffer.position(this.readPosition);
|
||||
buffer.limit(this.writePosition);
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +271,8 @@ public class DefaultDataBuffer implements DataBuffer {
|
|||
(oldBuffer.isDirect() ? ByteBuffer.allocateDirect(minCapacity) :
|
||||
ByteBuffer.allocate(minCapacity));
|
||||
|
||||
oldBuffer.position(this.readPosition);
|
||||
// Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
|
||||
((Buffer) oldBuffer).position(this.readPosition);
|
||||
newBuffer.put(oldBuffer);
|
||||
|
||||
this.byteBuffer = newBuffer;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.messaging.simp.stomp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -75,12 +76,12 @@ public class StompDecoder {
|
|||
* list of {@link Message}s. If the input buffer contains partial STOMP frame
|
||||
* content, or additional content with a partial STOMP frame, the buffer is
|
||||
* reset and {@code null} is returned.
|
||||
* @param buffer the buffer to decode the STOMP frame from
|
||||
* @param byteBuffer the buffer to decode the STOMP frame from
|
||||
* @return the decoded messages, or an empty list if none
|
||||
* @throws StompConversionException raised in case of decoding issues
|
||||
*/
|
||||
public List<Message<byte[]>> decode(ByteBuffer buffer) {
|
||||
return decode(buffer, null);
|
||||
public List<Message<byte[]>> decode(ByteBuffer byteBuffer) {
|
||||
return decode(byteBuffer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -95,17 +96,17 @@ public class StompDecoder {
|
|||
* headers in case of partial content. The caller can then check if a
|
||||
* "content-length" header was read, which helps to determine how much more
|
||||
* content is needed before the next attempt to decode.
|
||||
* @param buffer the buffer to decode the STOMP frame from
|
||||
* @param byteBuffer the buffer to decode the STOMP frame from
|
||||
* @param partialMessageHeaders an empty output map that will store the last
|
||||
* successfully parsed partialMessageHeaders in case of partial message content
|
||||
* in cases where the partial buffer ended with a partial STOMP frame
|
||||
* @return the decoded messages, or an empty list if none
|
||||
* @throws StompConversionException raised in case of decoding issues
|
||||
*/
|
||||
public List<Message<byte[]>> decode(ByteBuffer buffer, MultiValueMap<String, String> partialMessageHeaders) {
|
||||
public List<Message<byte[]>> decode(ByteBuffer byteBuffer, MultiValueMap<String, String> partialMessageHeaders) {
|
||||
List<Message<byte[]>> messages = new ArrayList<>();
|
||||
while (buffer.hasRemaining()) {
|
||||
Message<byte[]> message = decodeMessage(buffer, partialMessageHeaders);
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
Message<byte[]> message = decodeMessage(byteBuffer, partialMessageHeaders);
|
||||
if (message != null) {
|
||||
messages.add(message);
|
||||
}
|
||||
|
|
@ -119,21 +120,25 @@ public class StompDecoder {
|
|||
/**
|
||||
* Decode a single STOMP frame from the given {@code buffer} into a {@link Message}.
|
||||
*/
|
||||
private Message<byte[]> decodeMessage(ByteBuffer buffer, MultiValueMap<String, String> headers) {
|
||||
private Message<byte[]> decodeMessage(ByteBuffer byteBuffer, MultiValueMap<String, String> headers) {
|
||||
Message<byte[]> decodedMessage = null;
|
||||
skipLeadingEol(buffer);
|
||||
skipLeadingEol(byteBuffer);
|
||||
|
||||
// Explicit mark/reset access via Buffer base type for compatibility
|
||||
// with covariant return type on JDK 9's ByteBuffer...
|
||||
Buffer buffer = byteBuffer;
|
||||
buffer.mark();
|
||||
|
||||
String command = readCommand(buffer);
|
||||
String command = readCommand(byteBuffer);
|
||||
if (command.length() > 0) {
|
||||
StompHeaderAccessor headerAccessor = null;
|
||||
byte[] payload = null;
|
||||
if (buffer.remaining() > 0) {
|
||||
if (byteBuffer.remaining() > 0) {
|
||||
StompCommand stompCommand = StompCommand.valueOf(command);
|
||||
headerAccessor = StompHeaderAccessor.create(stompCommand);
|
||||
initHeaders(headerAccessor);
|
||||
readHeaders(buffer, headerAccessor);
|
||||
payload = readPayload(buffer, headerAccessor);
|
||||
readHeaders(byteBuffer, headerAccessor);
|
||||
payload = readPayload(byteBuffer, headerAccessor);
|
||||
}
|
||||
if (payload != null) {
|
||||
if (payload.length > 0 && !headerAccessor.getCommand().isBodyAllowed()) {
|
||||
|
|
@ -186,38 +191,38 @@ public class StompDecoder {
|
|||
* Skip one ore more EOL characters at the start of the given ByteBuffer.
|
||||
* Those are STOMP heartbeat frames.
|
||||
*/
|
||||
protected void skipLeadingEol(ByteBuffer buffer) {
|
||||
protected void skipLeadingEol(ByteBuffer byteBuffer) {
|
||||
while (true) {
|
||||
if (!tryConsumeEndOfLine(buffer)) {
|
||||
if (!tryConsumeEndOfLine(byteBuffer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readCommand(ByteBuffer buffer) {
|
||||
private String readCommand(ByteBuffer byteBuffer) {
|
||||
ByteArrayOutputStream command = new ByteArrayOutputStream(256);
|
||||
while (buffer.remaining() > 0 && !tryConsumeEndOfLine(buffer)) {
|
||||
command.write(buffer.get());
|
||||
while (byteBuffer.remaining() > 0 && !tryConsumeEndOfLine(byteBuffer)) {
|
||||
command.write(byteBuffer.get());
|
||||
}
|
||||
return new String(command.toByteArray(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private void readHeaders(ByteBuffer buffer, StompHeaderAccessor headerAccessor) {
|
||||
private void readHeaders(ByteBuffer byteBuffer, StompHeaderAccessor headerAccessor) {
|
||||
while (true) {
|
||||
ByteArrayOutputStream headerStream = new ByteArrayOutputStream(256);
|
||||
boolean headerComplete = false;
|
||||
while (buffer.hasRemaining()) {
|
||||
if (tryConsumeEndOfLine(buffer)) {
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
if (tryConsumeEndOfLine(byteBuffer)) {
|
||||
headerComplete = true;
|
||||
break;
|
||||
}
|
||||
headerStream.write(buffer.get());
|
||||
headerStream.write(byteBuffer.get());
|
||||
}
|
||||
if (headerStream.size() > 0 && headerComplete) {
|
||||
String header = new String(headerStream.toByteArray(), StandardCharsets.UTF_8);
|
||||
int colonIndex = header.indexOf(':');
|
||||
if (colonIndex <= 0) {
|
||||
if (buffer.remaining() > 0) {
|
||||
if (byteBuffer.remaining() > 0) {
|
||||
throw new StompConversionException("Illegal header: '" + header +
|
||||
"'. A header must be of the form <name>:[<value>].");
|
||||
}
|
||||
|
|
@ -229,7 +234,7 @@ public class StompDecoder {
|
|||
headerAccessor.addNativeHeader(headerName, headerValue);
|
||||
}
|
||||
catch (InvalidMimeTypeException ex) {
|
||||
if (buffer.remaining() > 0) {
|
||||
if (byteBuffer.remaining() > 0) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +285,7 @@ public class StompDecoder {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
private byte[] readPayload(ByteBuffer buffer, StompHeaderAccessor headerAccessor) {
|
||||
private byte[] readPayload(ByteBuffer byteBuffer, StompHeaderAccessor headerAccessor) {
|
||||
Integer contentLength;
|
||||
try {
|
||||
contentLength = headerAccessor.getContentLength();
|
||||
|
|
@ -291,10 +296,10 @@ public class StompDecoder {
|
|||
}
|
||||
|
||||
if (contentLength != null && contentLength >= 0) {
|
||||
if (buffer.remaining() > contentLength) {
|
||||
if (byteBuffer.remaining() > contentLength) {
|
||||
byte[] payload = new byte[contentLength];
|
||||
buffer.get(payload);
|
||||
if (buffer.get() != 0) {
|
||||
byteBuffer.get(payload);
|
||||
if (byteBuffer.get() != 0) {
|
||||
throw new StompConversionException("Frame must be terminated with a null octet");
|
||||
}
|
||||
return payload;
|
||||
|
|
@ -305,8 +310,8 @@ public class StompDecoder {
|
|||
}
|
||||
else {
|
||||
ByteArrayOutputStream payload = new ByteArrayOutputStream(256);
|
||||
while (buffer.remaining() > 0) {
|
||||
byte b = buffer.get();
|
||||
while (byteBuffer.remaining() > 0) {
|
||||
byte b = byteBuffer.get();
|
||||
if (b == 0) {
|
||||
return payload.toByteArray();
|
||||
}
|
||||
|
|
@ -322,21 +327,22 @@ public class StompDecoder {
|
|||
* Try to read an EOL incrementing the buffer position if successful.
|
||||
* @return whether an EOL was consumed
|
||||
*/
|
||||
private boolean tryConsumeEndOfLine(ByteBuffer buffer) {
|
||||
if (buffer.remaining() > 0) {
|
||||
byte b = buffer.get();
|
||||
private boolean tryConsumeEndOfLine(ByteBuffer byteBuffer) {
|
||||
if (byteBuffer.remaining() > 0) {
|
||||
byte b = byteBuffer.get();
|
||||
if (b == '\n') {
|
||||
return true;
|
||||
}
|
||||
else if (b == '\r') {
|
||||
if (buffer.remaining() > 0 && buffer.get() == '\n') {
|
||||
if (byteBuffer.remaining() > 0 && byteBuffer.get() == '\n') {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
throw new StompConversionException("'\\r' must be followed by '\\n'");
|
||||
}
|
||||
}
|
||||
buffer.position(buffer.position() - 1);
|
||||
// Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
|
||||
((Buffer) byteBuffer).position(byteBuffer.position() - 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue