Merge branch '5.1.x'
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -32,6 +33,8 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link Decoder} unit tests. Subclasses need to implement
|
||||
* {@link #canDecode()}, {@link #decode()} and {@link #decodeToMono()}, possibly using the wide
|
||||
@@ -99,6 +102,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
*/
|
||||
protected <T> void testDecodeAll(Publisher<DataBuffer> input, Class<? extends T> outputClass,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
|
||||
testDecodeAll(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
@@ -122,6 +126,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
protected <T> void testDecodeAll(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
testDecode(input, outputType, stepConsumer, mimeType, hints);
|
||||
testDecodeError(input, outputType, mimeType, hints);
|
||||
testDecodeCancel(input, outputType, mimeType, hints);
|
||||
@@ -151,6 +156,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
*/
|
||||
protected <T> void testDecode(Publisher<DataBuffer> input, Class<? extends T> outputClass,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
|
||||
testDecode(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
@@ -202,16 +208,14 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
protected void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextCount(1)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
input = Mono.from(input).concatWith(Flux.error(new InputException()));
|
||||
try {
|
||||
this.decoder.decode(input, outputType, mimeType, hints).blockLast(Duration.ofSeconds(5));
|
||||
fail();
|
||||
}
|
||||
catch (InputException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,11 +233,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextCount(1)
|
||||
.thenCancel()
|
||||
.verify();
|
||||
StepVerifier.create(result).expectNextCount(1).thenCancel().verify();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,9 +249,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
|
||||
Flux<DataBuffer> input = Flux.empty();
|
||||
Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
StepVerifier.create(result).verifyComplete();
|
||||
}
|
||||
|
||||
// Mono
|
||||
@@ -297,6 +295,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
protected <T> void testDecodeToMonoAll(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
testDecodeToMono(input, outputType, stepConsumer, mimeType, hints);
|
||||
testDecodeToMonoError(input, outputType, mimeType, hints);
|
||||
testDecodeToMonoCancel(input, outputType, mimeType, hints);
|
||||
@@ -326,6 +325,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
*/
|
||||
protected <T> void testDecodeToMono(Publisher<DataBuffer> input,
|
||||
Class<? extends T> outputClass, Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
|
||||
testDecodeToMono(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
@@ -377,15 +377,9 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
protected void testDecodeToMonoError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
input = Mono.from(input).concatWith(Flux.error(new InputException()));
|
||||
Mono<?> result = this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
StepVerifier.create(result).expectError(InputException.class).verify();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,10 +395,7 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Mono<?> result = this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.thenCancel()
|
||||
.verify();
|
||||
StepVerifier.create(result).thenCancel().verify();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,11 +409,8 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
protected void testDecodeToMonoEmpty(ResolvableType outputType, @Nullable MimeType mimeType,
|
||||
@Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<DataBuffer> input = Flux.empty();
|
||||
Mono<?> result = this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
Mono<?> result = this.decoder.decodeToMono(Flux.empty(), outputType, mimeType, hints);
|
||||
StepVerifier.create(result).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,10 +419,10 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
* @return the deferred buffer
|
||||
*/
|
||||
protected Mono<DataBuffer> dataBuffer(byte[] bytes) {
|
||||
return Mono.defer(() -> {
|
||||
return Mono.fromCallable(() -> {
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
dataBuffer.write(bytes);
|
||||
return Mono.just(dataBuffer);
|
||||
return dataBuffer;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -442,9 +430,6 @@ public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
* Exception used in {@link #testDecodeError} and {@link #testDecodeToMonoError}
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class InputException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
public static class InputException extends RuntimeException {}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,25 +19,20 @@ package org.springframework.core.codec;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
import static org.springframework.core.ResolvableType.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
@@ -67,9 +62,7 @@ public class ResourceDecoderTests extends AbstractDecoderTestCase<ResourceDecode
|
||||
@Override
|
||||
@Test
|
||||
public void decode() {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
Flux<DataBuffer> input = Flux.concat(dataBuffer(this.fooBytes), dataBuffer(this.barBytes));
|
||||
|
||||
testDecodeAll(input, Resource.class, step -> step
|
||||
.consumeNextWith(resource -> {
|
||||
@@ -85,21 +78,6 @@ public class ResourceDecoderTests extends AbstractDecoderTestCase<ResourceDecode
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Flux<Resource> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decodeToMono() {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.core.codec;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Subscription;
|
||||
@@ -34,7 +33,6 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.LeakAwareDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.core.io.support.ResourceRegion;
|
||||
import org.springframework.util.MimeType;
|
||||
@@ -51,8 +49,7 @@ public class ResourceRegionEncoderTests {
|
||||
|
||||
private ResourceRegionEncoder encoder = new ResourceRegionEncoder();
|
||||
|
||||
private LeakAwareDataBufferFactory bufferFactory =
|
||||
new LeakAwareDataBufferFactory(new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT));
|
||||
private LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory();
|
||||
|
||||
|
||||
@After
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -19,18 +19,16 @@ package org.springframework.core.codec;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
@@ -61,10 +59,8 @@ public class StringDecoderTests extends AbstractDecoderTestCase<StringDecoder> {
|
||||
assertTrue(this.decoder.canDecode(TYPE, MimeTypeUtils.TEXT_HTML));
|
||||
assertTrue(this.decoder.canDecode(TYPE, MimeTypeUtils.APPLICATION_JSON));
|
||||
assertTrue(this.decoder.canDecode(TYPE, MimeTypeUtils.parseMimeType("text/plain;charset=utf-8")));
|
||||
assertFalse(this.decoder.canDecode(
|
||||
ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN));
|
||||
assertFalse(this.decoder.canDecode(
|
||||
ResolvableType.forClass(Object.class), MimeTypeUtils.APPLICATION_JSON));
|
||||
assertFalse(this.decoder.canDecode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN));
|
||||
assertFalse(this.decoder.canDecode(ResolvableType.forClass(Object.class), MimeTypeUtils.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,24 +72,7 @@ public class StringDecoderTests extends AbstractDecoderTestCase<StringDecoder> {
|
||||
String s = String.format("%s\n%s\n%s", u, e, o);
|
||||
Flux<DataBuffer> input = toDataBuffers(s, 1, UTF_8);
|
||||
|
||||
testDecodeAll(input, ResolvableType.forClass(String.class), step -> step
|
||||
.expectNext(u, e, o)
|
||||
.verifyComplete(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Flux<String> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
testDecodeAll(input, TYPE, step -> step.expectNext(u, e, o).verifyComplete(), null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,21 +84,21 @@ public class StringDecoderTests extends AbstractDecoderTestCase<StringDecoder> {
|
||||
Flux<DataBuffer> source = toDataBuffers(s, 2, UTF_16BE);
|
||||
MimeType mimeType = MimeTypeUtils.parseMimeType("text/plain;charset=utf-16be");
|
||||
|
||||
testDecode(source, TYPE, step -> step
|
||||
.expectNext(u, e, o)
|
||||
.verifyComplete(), mimeType, null);
|
||||
testDecode(source, TYPE, step -> step.expectNext(u, e, o).verifyComplete(), mimeType, null);
|
||||
}
|
||||
|
||||
private Flux<DataBuffer> toDataBuffers(String s, int length, Charset charset) {
|
||||
byte[] bytes = s.getBytes(charset);
|
||||
|
||||
List<DataBuffer> dataBuffers = new ArrayList<>();
|
||||
List<byte[]> chunks = new ArrayList<>();
|
||||
for (int i = 0; i < bytes.length; i += length) {
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(length);
|
||||
dataBuffer.write(bytes, i, length);
|
||||
dataBuffers.add(dataBuffer);
|
||||
chunks.add(Arrays.copyOfRange(bytes, i, i + length));
|
||||
}
|
||||
return Flux.fromIterable(dataBuffers);
|
||||
return Flux.fromIterable(chunks)
|
||||
.map(chunk -> {
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(length);
|
||||
dataBuffer.write(chunk, 0, chunk.length);
|
||||
return dataBuffer;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -63,6 +63,11 @@ class LeakAwareDataBuffer implements PooledDataBuffer {
|
||||
return this.leakError;
|
||||
}
|
||||
|
||||
|
||||
public DataBuffer getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllocated() {
|
||||
return this.delegate instanceof PooledDataBuffer &&
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -55,7 +56,7 @@ public class LeakAwareDataBufferFactory implements DataBufferFactory {
|
||||
* {@link DefaultDataBufferFactory}.
|
||||
*/
|
||||
public LeakAwareDataBufferFactory() {
|
||||
this(new DefaultDataBufferFactory());
|
||||
this(new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +68,7 @@ public class LeakAwareDataBufferFactory implements DataBufferFactory {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether all of the data buffers allocated by this factory have also been released.
|
||||
* If not, then an {@link AssertionError} is thrown. Typically used from a JUnit {@link After}
|
||||
@@ -126,6 +128,10 @@ public class LeakAwareDataBufferFactory implements DataBufferFactory {
|
||||
|
||||
@Override
|
||||
public DataBuffer join(List<? extends DataBuffer> dataBuffers) {
|
||||
// Remove LeakAwareDataBuffer wrapper so delegate can find native buffers
|
||||
dataBuffers = dataBuffers.stream()
|
||||
.map(o -> o instanceof LeakAwareDataBuffer ? ((LeakAwareDataBuffer) o).getDelegate() : o)
|
||||
.collect(Collectors.toList());
|
||||
return new LeakAwareDataBuffer(this.delegate.join(dataBuffers), this);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user