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:
Juergen Hoeller 2016-10-05 00:10:06 +02:00
parent 2fea1f7da3
commit ffc781b163
2 changed files with 60 additions and 44 deletions

View File

@ -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;

View File

@ -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;
}