streamingMediaTypes = new ArrayList<>(1);
-
+public class Jackson2JsonEncoder extends AbstractJackson2Encoder {
+
private final PrettyPrinter ssePrettyPrinter;
-
-
+
public Jackson2JsonEncoder() {
this(Jackson2ObjectMapperBuilder.json().build());
@@ -85,113 +62,16 @@ public class Jackson2JsonEncoder extends Jackson2CodecSupport implements HttpMes
return printer;
}
-
- /**
- * Configure "streaming" media types for which flushing should be performed
- * automatically vs at the end of the stream.
- * By default this is set to {@link MediaType#APPLICATION_STREAM_JSON}.
- * @param mediaTypes one or more media types to add to the list
- * @see HttpMessageEncoder#getStreamingMediaTypes()
- */
- public void setStreamingMediaTypes(List mediaTypes) {
- this.streamingMediaTypes.clear();
- this.streamingMediaTypes.addAll(mediaTypes);
+ @Override
+ protected ObjectWriter customizeWriter(ObjectWriter writer, @Nullable MimeType mimeType,
+ ResolvableType elementType, @Nullable Map hints) {
+
+ return (this.ssePrettyPrinter != null && MediaType.TEXT_EVENT_STREAM.isCompatibleWith(mimeType) &&
+ writer.getConfig().isEnabled(SerializationFeature.INDENT_OUTPUT) ? writer.with(this.ssePrettyPrinter) : writer);
}
@Override
public List getEncodableMimeTypes() {
return JSON_MIME_TYPES;
}
-
-
- @Override
- public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) {
- Class> clazz = elementType.resolve(Object.class);
- return (Object.class == clazz) ||
- !String.class.isAssignableFrom(elementType.resolve(clazz)) &&
- objectMapper().canSerialize(clazz) && supportsMimeType(mimeType);
- }
-
- @Override
- public Flux encode(Publisher> inputStream, DataBufferFactory bufferFactory,
- ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) {
-
- Assert.notNull(inputStream, "'inputStream' must not be null");
- Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
- Assert.notNull(elementType, "'elementType' must not be null");
-
- if (inputStream instanceof Mono) {
- return Flux.from(inputStream).map(value ->
- encodeValue(value, mimeType, bufferFactory, elementType, hints));
- }
- else if (this.streamingMediaTypes.stream().anyMatch(streamingMediaType -> streamingMediaType.isCompatibleWith(mimeType))) {
- return Flux.from(inputStream).map(value -> {
- DataBuffer buffer = encodeValue(value, mimeType, bufferFactory, elementType, hints);
- buffer.write(new byte[]{'\n'});
- return buffer;
- });
- }
- else {
- ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
- return Flux.from(inputStream).collectList().map(list ->
- encodeValue(list, mimeType, bufferFactory, listType, hints)).flux();
- }
- }
-
- private DataBuffer encodeValue(Object value, @Nullable MimeType mimeType, DataBufferFactory bufferFactory,
- ResolvableType elementType, @Nullable Map hints) {
-
- JavaType javaType = getJavaType(elementType.getType(), null);
- Class> jsonView = (hints != null ? (Class>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
- ObjectWriter writer = (jsonView != null ?
- objectMapper().writerWithView(jsonView) : objectMapper().writer());
-
- if (javaType.isContainerType()) {
- writer = writer.forType(javaType);
- }
-
- if (MediaType.TEXT_EVENT_STREAM.isCompatibleWith(mimeType) &&
- writer.getConfig().isEnabled(SerializationFeature.INDENT_OUTPUT)) {
-
- writer = writer.with(this.ssePrettyPrinter);
- }
-
- DataBuffer buffer = bufferFactory.allocateBuffer();
- OutputStream outputStream = buffer.asOutputStream();
- try {
- writer.writeValue(outputStream, value);
- }
- catch (InvalidDefinitionException ex) {
- throw new CodecException("Type definition error: " + ex.getType(), ex);
- }
- catch (JsonProcessingException ex) {
- throw new EncodingException("JSON encoding error: " + ex.getOriginalMessage(), ex);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Unexpected I/O error while writing to data buffer", ex);
- }
-
- return buffer;
- }
-
-
- // HttpMessageEncoder...
-
- @Override
- public List getStreamingMediaTypes() {
- return Collections.unmodifiableList(this.streamingMediaTypes);
- }
-
- @Override
- public Map getEncodeHints(@Nullable ResolvableType actualType, ResolvableType elementType,
- @Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) {
-
- return (actualType != null ? getHints(actualType) : Collections.emptyMap());
- }
-
- @Override
- protected A getAnnotation(MethodParameter parameter, Class annotType) {
- return parameter.getMethodAnnotation(annotType);
- }
-
}
diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2SmileDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2SmileDecoder.java
new file mode 100644
index 0000000000..0e15943552
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2SmileDecoder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2017 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
+ *
+ * http://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.http.codec.json;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.smile.SmileFactory;
+
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.Assert;
+import org.springframework.util.MimeType;
+
+/**
+ * Decode a byte stream into Smile and convert to Object's with Jackson 2.9.
+ *
+ * @author Sebastien Deleuze
+ * @author Rossen Stoyanchev
+ * @since 5.0
+ * @see Jackson2JsonEncoder
+ */
+public class Jackson2SmileDecoder extends AbstractJackson2Decoder {
+
+ private static final MimeType SMILE_MIME_TYPE = new MediaType("application", "x-jackson-smile");
+
+
+ public Jackson2SmileDecoder() {
+ this(Jackson2ObjectMapperBuilder.smile().build(), SMILE_MIME_TYPE);
+ }
+
+ public Jackson2SmileDecoder(ObjectMapper mapper, MimeType... mimeTypes) {
+ super(mapper, mimeTypes);
+ Assert.isAssignable(SmileFactory.class, mapper.getFactory().getClass());
+ }
+
+ @Override
+ public List getDecodableMimeTypes() {
+ return Arrays.asList(SMILE_MIME_TYPE);
+ }
+}
diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2SmileEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2SmileEncoder.java
new file mode 100644
index 0000000000..4614aa4364
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2SmileEncoder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2017 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
+ *
+ * http://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.http.codec.json;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.smile.SmileFactory;
+
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.Assert;
+import org.springframework.util.MimeType;
+
+/**
+ * Encode from an {@code Object} stream to a byte stream of Smile objects using Jackson 2.9.
+ *
+ * @author Sebastien Deleuze
+ * @since 5.0
+ * @see Jackson2SmileDecoder
+ */
+public class Jackson2SmileEncoder extends AbstractJackson2Encoder {
+
+ private static final MimeType SMILE_MIME_TYPE = new MediaType("application", "x-jackson-smile");
+
+
+ public Jackson2SmileEncoder() {
+ this(Jackson2ObjectMapperBuilder.smile().build(), new MediaType("application", "x-jackson-smile"));
+ }
+
+ public Jackson2SmileEncoder(ObjectMapper mapper, MimeType... mimeTypes) {
+ super(mapper, mimeTypes);
+ Assert.isAssignable(SmileFactory.class, mapper.getFactory().getClass());
+ this.streamingMediaTypes.add(new MediaType("application", "stream+x-jackson-smile"));
+ }
+
+ @Override
+ public List getEncodableMimeTypes() {
+ return Arrays.asList(SMILE_MIME_TYPE);
+ }
+}
diff --git a/spring-web/src/test/java/org/springframework/http/codec/ClientCodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/ClientCodecConfigurerTests.java
index 3bee998273..a1493cbd06 100644
--- a/spring-web/src/test/java/org/springframework/http/codec/ClientCodecConfigurerTests.java
+++ b/spring-web/src/test/java/org/springframework/http/codec/ClientCodecConfigurerTests.java
@@ -40,6 +40,8 @@ import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
+import org.springframework.http.codec.json.Jackson2SmileDecoder;
+import org.springframework.http.codec.json.Jackson2SmileEncoder;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
@@ -62,7 +64,7 @@ public class ClientCodecConfigurerTests {
@Test
public void defaultReaders() throws Exception {
List> readers = this.configurer.getReaders();
- assertEquals(9, readers.size());
+ assertEquals(10, readers.size());
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
@@ -70,6 +72,7 @@ public class ClientCodecConfigurerTests {
assertStringDecoder(getNextDecoder(readers), true);
assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass());
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());
+ assertEquals(Jackson2SmileDecoder.class, getNextDecoder(readers).getClass());
assertSseReader(readers);
assertStringDecoder(getNextDecoder(readers), false);
}
@@ -77,7 +80,7 @@ public class ClientCodecConfigurerTests {
@Test
public void defaultWriters() throws Exception {
List> writers = this.configurer.getWriters();
- assertEquals(10, writers.size());
+ assertEquals(11, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
@@ -87,6 +90,7 @@ public class ClientCodecConfigurerTests {
assertEquals(MultipartHttpMessageWriter.class, writers.get(this.index.getAndIncrement()).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
+ assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertStringEncoder(getNextEncoder(writers), false);
}
@@ -94,7 +98,7 @@ public class ClientCodecConfigurerTests {
public void jackson2EncoderOverride() throws Exception {
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
- this.configurer.defaultCodecs().jackson2Decoder(decoder);
+ this.configurer.defaultCodecs().jackson2JsonDecoder(decoder);
assertSame(decoder, this.configurer.getReaders().stream()
.filter(reader -> ServerSentEventHttpMessageReader.class.equals(reader.getClass()))
diff --git a/spring-web/src/test/java/org/springframework/http/codec/CodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/CodecConfigurerTests.java
index 4c33e9b483..5afc47b407 100644
--- a/spring-web/src/test/java/org/springframework/http/codec/CodecConfigurerTests.java
+++ b/spring-web/src/test/java/org/springframework/http/codec/CodecConfigurerTests.java
@@ -35,6 +35,8 @@ import org.springframework.core.codec.StringDecoder;
import org.springframework.http.MediaType;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
+import org.springframework.http.codec.json.Jackson2SmileDecoder;
+import org.springframework.http.codec.json.Jackson2SmileEncoder;
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
import org.springframework.util.MimeTypeUtils;
@@ -60,7 +62,7 @@ public class CodecConfigurerTests {
@Test
public void defaultReaders() throws Exception {
List> readers = this.configurer.getReaders();
- assertEquals(8, readers.size());
+ assertEquals(9, readers.size());
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
@@ -68,13 +70,14 @@ public class CodecConfigurerTests {
assertStringDecoder(getNextDecoder(readers), true);
assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass());
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());
+ assertEquals(Jackson2SmileDecoder.class, getNextDecoder(readers).getClass());
assertStringDecoder(getNextDecoder(readers), false);
}
@Test
public void defaultWriters() throws Exception {
List> writers = this.configurer.getWriters();
- assertEquals(8, writers.size());
+ assertEquals(9, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
@@ -82,6 +85,7 @@ public class CodecConfigurerTests {
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
+ assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertStringEncoder(getNextEncoder(writers), false);
}
@@ -108,7 +112,7 @@ public class CodecConfigurerTests {
List> readers = this.configurer.getReaders();
- assertEquals(12, readers.size());
+ assertEquals(13, readers.size());
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
@@ -118,6 +122,7 @@ public class CodecConfigurerTests {
assertSame(customReader1, readers.get(this.index.getAndIncrement()));
assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass());
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());
+ assertEquals(Jackson2SmileDecoder.class, getNextDecoder(readers).getClass());
assertSame(customDecoder2, getNextDecoder(readers));
assertSame(customReader2, readers.get(this.index.getAndIncrement()));
assertEquals(StringDecoder.class, getNextDecoder(readers).getClass());
@@ -146,7 +151,7 @@ public class CodecConfigurerTests {
List> writers = this.configurer.getWriters();
- assertEquals(12, writers.size());
+ assertEquals(13, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
@@ -156,6 +161,7 @@ public class CodecConfigurerTests {
assertSame(customWriter1, writers.get(this.index.getAndIncrement()));
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
+ assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertSame(customEncoder2, getNextEncoder(writers));
assertSame(customWriter2, writers.get(this.index.getAndIncrement()));
assertEquals(CharSequenceEncoder.class, getNextEncoder(writers).getClass());
@@ -229,7 +235,7 @@ public class CodecConfigurerTests {
public void jackson2DecoderOverride() throws Exception {
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
- this.configurer.defaultCodecs().jackson2Decoder(decoder);
+ this.configurer.defaultCodecs().jackson2JsonDecoder(decoder);
assertSame(decoder, this.configurer.getReaders().stream()
.filter(writer -> writer instanceof DecoderHttpMessageReader)
@@ -243,7 +249,7 @@ public class CodecConfigurerTests {
public void jackson2EncoderOverride() throws Exception {
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
- this.configurer.defaultCodecs().jackson2Encoder(encoder);
+ this.configurer.defaultCodecs().jackson2JsonEncoder(encoder);
assertSame(encoder, this.configurer.getWriters().stream()
.filter(writer -> writer instanceof EncoderHttpMessageWriter)
diff --git a/spring-web/src/test/java/org/springframework/http/codec/ServerCodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/ServerCodecConfigurerTests.java
index 07c8c5f08f..6551d51643 100644
--- a/spring-web/src/test/java/org/springframework/http/codec/ServerCodecConfigurerTests.java
+++ b/spring-web/src/test/java/org/springframework/http/codec/ServerCodecConfigurerTests.java
@@ -41,6 +41,8 @@ import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
+import org.springframework.http.codec.json.Jackson2SmileDecoder;
+import org.springframework.http.codec.json.Jackson2SmileEncoder;
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader;
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
@@ -64,7 +66,7 @@ public class ServerCodecConfigurerTests {
@Test
public void defaultReaders() throws Exception {
List> readers = this.configurer.getReaders();
- assertEquals(11, readers.size());
+ assertEquals(12, readers.size());
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
@@ -75,13 +77,14 @@ public class ServerCodecConfigurerTests {
assertEquals(MultipartHttpMessageReader.class, readers.get(this.index.getAndIncrement()).getClass());
assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass());
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());
+ assertEquals(Jackson2SmileDecoder.class, getNextDecoder(readers).getClass());
assertStringDecoder(getNextDecoder(readers), false);
}
@Test
public void defaultWriters() throws Exception {
List> writers = this.configurer.getWriters();
- assertEquals(9, writers.size());
+ assertEquals(10, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
@@ -89,6 +92,7 @@ public class ServerCodecConfigurerTests {
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
+ assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertSseWriter(writers);
assertStringEncoder(getNextEncoder(writers), false);
}
@@ -97,7 +101,7 @@ public class ServerCodecConfigurerTests {
public void jackson2EncoderOverride() throws Exception {
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
- this.configurer.defaultCodecs().jackson2Encoder(encoder);
+ this.configurer.defaultCodecs().jackson2JsonEncoder(encoder);
assertSame(encoder, this.configurer.getWriters().stream()
.filter(writer -> ServerSentEventHttpMessageWriter.class.equals(writer.getClass()))
diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java
new file mode 100644
index 0000000000..1a20f91314
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002-2017 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
+ *
+ * http://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.http.codec.json;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.codec.CodecException;
+import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.codec.Pojo;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.MimeType;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyMap;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.core.ResolvableType.forClass;
+import static org.springframework.http.MediaType.APPLICATION_JSON;
+
+/**
+ * Unit tests for {@link Jackson2SmileDecoder}.
+ *
+ * @author Sebastien Deleuze
+ */
+public class Jackson2SmileDecoderTests extends AbstractDataBufferAllocatingTestCase {
+
+ private final static MimeType SMILE_MIME_TYPE = new MimeType("application", "x-jackson-smile");
+
+ private final Jackson2SmileDecoder decoder = new Jackson2SmileDecoder();
+
+ @Test
+ public void canDecode() {
+ assertTrue(decoder.canDecode(forClass(Pojo.class), SMILE_MIME_TYPE));
+ assertTrue(decoder.canDecode(forClass(Pojo.class), null));
+
+ assertFalse(decoder.canDecode(forClass(String.class), null));
+ assertFalse(decoder.canDecode(forClass(Pojo.class), APPLICATION_JSON));
+ }
+
+ @Test
+ public void decodePojo() throws Exception {
+ ObjectMapper mapper = Jackson2ObjectMapperBuilder.smile().build();
+ Pojo pojo = new Pojo("foo", "bar");
+ byte[] serializedPojo = mapper.writer().writeValueAsBytes(pojo);
+
+ Flux source = Flux.just(this.bufferFactory.wrap(serializedPojo));
+ ResolvableType elementType = forClass(Pojo.class);
+ Flux flux = decoder.decode(source, elementType, null, emptyMap());
+
+ StepVerifier.create(flux)
+ .expectNext(pojo)
+ .verifyComplete();
+ }
+
+ @Test
+ public void decodePojoWithError() throws Exception {
+ Flux source = Flux.just(stringBuffer("123"));
+ ResolvableType elementType = forClass(Pojo.class);
+ Flux flux = decoder.decode(source, elementType, null, emptyMap());
+
+ StepVerifier.create(flux).verifyError(CodecException.class);
+ }
+
+ @Test
+ public void decodeToList() throws Exception {
+ ObjectMapper mapper = Jackson2ObjectMapperBuilder.smile().build();
+ List list = asList(new Pojo("f1", "b1"), new Pojo("f2", "b2"));
+ byte[] serializedList = mapper.writer().writeValueAsBytes(list);
+ Flux source = Flux.just(this.bufferFactory.wrap(serializedList));
+
+ ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class);
+ Mono mono = decoder.decodeToMono(source, elementType, null, emptyMap());
+
+ StepVerifier.create(mono)
+ .expectNext(list)
+ .expectComplete()
+ .verify();
+ }
+
+ @Test
+ public void decodeToFlux() throws Exception {
+ ObjectMapper mapper = Jackson2ObjectMapperBuilder.smile().build();
+ List list = asList(new Pojo("f1", "b1"), new Pojo("f2", "b2"));
+ byte[] serializedList = mapper.writer().writeValueAsBytes(list);
+ Flux source = Flux.just(this.bufferFactory.wrap(serializedList));
+
+ ResolvableType elementType = forClass(Pojo.class);
+ Flux flux = decoder.decode(source, elementType, null, emptyMap());
+
+ StepVerifier.create(flux)
+ .expectNext(new Pojo("f1", "b1"))
+ .expectNext(new Pojo("f2", "b2"))
+ .verifyComplete();
+ }
+
+}
diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java
new file mode 100644
index 0000000000..1533589c6f
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2002-2017 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
+ *
+ * http://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.http.codec.json;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.core.io.buffer.support.DataBufferTestUtils;
+import org.springframework.http.MediaType;
+import org.springframework.http.codec.Pojo;
+import org.springframework.http.codec.ServerSentEvent;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.MimeType;
+
+import static java.util.Collections.emptyMap;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.http.MediaType.APPLICATION_XML;
+
+/**
+ * Unit tests for {@link Jackson2SmileEncoder}.
+ *
+ * @author Sebastien Deleuze
+ */
+public class Jackson2SmileEncoderTests extends AbstractDataBufferAllocatingTestCase {
+
+ private final static MimeType SMILE_MIME_TYPE = new MimeType("application", "x-jackson-smile");
+
+ private final Jackson2SmileEncoder encoder = new Jackson2SmileEncoder();
+
+
+ @Test
+ public void canEncode() {
+ ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
+ assertTrue(this.encoder.canEncode(pojoType, SMILE_MIME_TYPE));
+ assertTrue(this.encoder.canEncode(pojoType, null));
+
+ // SPR-15464
+ assertTrue(this.encoder.canEncode(ResolvableType.NONE, null));
+ }
+
+ @Test
+ public void canNotEncode() {
+ assertFalse(this.encoder.canEncode(ResolvableType.forClass(String.class), null));
+ assertFalse(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), APPLICATION_XML));
+
+ ResolvableType sseType = ResolvableType.forClass(ServerSentEvent.class);
+ assertFalse(this.encoder.canEncode(sseType, SMILE_MIME_TYPE));
+ }
+
+ @Test
+ public void encode() throws Exception {
+ Flux source = Flux.just(
+ new Pojo("foo", "bar"),
+ new Pojo("foofoo", "barbar"),
+ new Pojo("foofoofoo", "barbarbar")
+ );
+ ResolvableType type = ResolvableType.forClass(Pojo.class);
+ Flux output = this.encoder.encode(source, this.bufferFactory, type, null, emptyMap());
+
+ ObjectMapper mapper = Jackson2ObjectMapperBuilder.smile().build();
+ StepVerifier.create(output)
+ .consumeNextWith(dataBuffer -> readPojo(mapper, List.class, dataBuffer))
+ .verifyComplete();
+ }
+
+ @Test
+ public void encodeAsStream() throws Exception {
+ Flux source = Flux.just(
+ new Pojo("foo", "bar"),
+ new Pojo("foofoo", "barbar"),
+ new Pojo("foofoofoo", "barbarbar")
+ );
+ ResolvableType type = ResolvableType.forClass(Pojo.class);
+ MediaType mediaType = new MediaType("application", "stream+x-jackson-smile");
+ Flux output = this.encoder.encode(source, this.bufferFactory, type, mediaType, emptyMap());
+
+ ObjectMapper mapper = Jackson2ObjectMapperBuilder.smile().build();
+ StepVerifier.create(output)
+ .consumeNextWith(dataBuffer -> readPojo(mapper, Pojo.class, dataBuffer))
+ .consumeNextWith(dataBuffer -> readPojo(mapper, Pojo.class, dataBuffer))
+ .consumeNextWith(dataBuffer -> readPojo(mapper, Pojo.class, dataBuffer))
+ .verifyComplete();
+ }
+
+ public T readPojo(ObjectMapper mapper, Class valueType, DataBuffer dataBuffer) {
+ try {
+ T value = mapper.reader().forType(valueType).readValue(DataBufferTestUtils.dumpBytes(dataBuffer));
+ DataBufferUtils.release(dataBuffer);
+ return value;
+ }
+ catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ }
+
+}
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
index 8e5204f8eb..408a6da1da 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
@@ -97,7 +97,7 @@ public class DelegatingWebFluxConfigurationTests {
verify(webFluxConfigurer).configureArgumentResolvers(any());
assertSame(formatterRegistry.getValue(), initializerConversionService);
- assertEquals(11, codecsConfigurer.getValue().getReaders().size());
+ assertEquals(12, codecsConfigurer.getValue().getReaders().size());
}
@Test
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java
index f700e7f1c1..ed08ee0744 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java
@@ -138,7 +138,7 @@ public class WebFluxConfigurationSupportTests {
assertNotNull(adapter);
List> readers = adapter.getMessageCodecConfigurer().getReaders();
- assertEquals(11, readers.size());
+ assertEquals(12, readers.size());
assertHasMessageReader(readers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
assertHasMessageReader(readers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);
@@ -147,6 +147,7 @@ public class WebFluxConfigurationSupportTests {
assertHasMessageReader(readers, forClassWithGenerics(MultiValueMap.class, String.class, String.class), APPLICATION_FORM_URLENCODED);
assertHasMessageReader(readers, forClass(TestBean.class), APPLICATION_XML);
assertHasMessageReader(readers, forClass(TestBean.class), APPLICATION_JSON);
+ assertHasMessageReader(readers, forClass(TestBean.class), new MediaType("application", "x-jackson-smile"));
assertHasMessageReader(readers, forClass(TestBean.class), null);
WebBindingInitializer bindingInitializer = adapter.getWebBindingInitializer();
@@ -189,7 +190,7 @@ public class WebFluxConfigurationSupportTests {
assertEquals(0, handler.getOrder());
List> writers = handler.getMessageWriters();
- assertEquals(9, writers.size());
+ assertEquals(10, writers.size());
assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);
@@ -197,6 +198,7 @@ public class WebFluxConfigurationSupportTests {
assertHasMessageWriter(writers, forClass(Resource.class), IMAGE_PNG);
assertHasMessageWriter(writers, forClass(TestBean.class), APPLICATION_XML);
assertHasMessageWriter(writers, forClass(TestBean.class), APPLICATION_JSON);
+ assertHasMessageWriter(writers, forClass(TestBean.class), new MediaType("application", "x-jackson-smile"));
assertHasMessageWriter(writers, forClass(TestBean.class), MediaType.parseMediaType("text/event-stream"));
name = "webFluxContentTypeResolver";
@@ -215,7 +217,7 @@ public class WebFluxConfigurationSupportTests {
assertEquals(100, handler.getOrder());
List> writers = handler.getMessageWriters();
- assertEquals(9, writers.size());
+ assertEquals(10, writers.size());
assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);
@@ -223,6 +225,7 @@ public class WebFluxConfigurationSupportTests {
assertHasMessageWriter(writers, forClass(Resource.class), IMAGE_PNG);
assertHasMessageWriter(writers, forClass(TestBean.class), APPLICATION_XML);
assertHasMessageWriter(writers, forClass(TestBean.class), APPLICATION_JSON);
+ assertHasMessageWriter(writers, forClass(TestBean.class), new MediaType("application", "x-jackson-smile"));
assertHasMessageWriter(writers, forClass(TestBean.class), null);
name = "webFluxContentTypeResolver";