diff --git a/spring-core/src/main/java/org/springframework/core/io/buffer/DefaultDataBuffer.java b/spring-core/src/main/java/org/springframework/core/io/buffer/DefaultDataBuffer.java index 24d707a2d9b..16f14a43ada 100644 --- a/spring-core/src/main/java/org/springframework/core/io/buffer/DefaultDataBuffer.java +++ b/spring-core/src/main/java/org/springframework/core/io/buffer/DefaultDataBuffer.java @@ -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 readInternal(Function 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 writeInternal(Function 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; diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java index cd35ed13aae..d7ab7a80aeb 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java @@ -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> decode(ByteBuffer buffer) { - return decode(buffer, null); + public List> 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> decode(ByteBuffer buffer, MultiValueMap partialMessageHeaders) { + public List> decode(ByteBuffer byteBuffer, MultiValueMap partialMessageHeaders) { List> messages = new ArrayList<>(); - while (buffer.hasRemaining()) { - Message message = decodeMessage(buffer, partialMessageHeaders); + while (byteBuffer.hasRemaining()) { + Message 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 decodeMessage(ByteBuffer buffer, MultiValueMap headers) { + private Message decodeMessage(ByteBuffer byteBuffer, MultiValueMap headers) { Message 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 :[]."); } @@ -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; }