From 66476559edf640cd2c849d17247dc761a5c10adb Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 21 May 2018 08:50:35 +0100 Subject: [PATCH] Move JsonMapper to spring-cloud-function-context ...in case it is needed elsewhere (see gh-151) --- spring-cloud-function-context/pom.xml | 10 ++++ ...ntextFunctionCatalogAutoConfiguration.java | 49 ++++++++++++++++++ .../cloud/function/json}/GsonMapper.java | 7 ++- .../cloud/function/json}/JacksonMapper.java | 14 +++++- .../cloud/function/json}/JsonMapper.java | 4 +- .../cloud/function/util/JsonMapperTests.java | 18 +++++-- .../web/flux/ReactorAutoConfiguration.java | 50 +------------------ .../FluxHandlerMethodArgumentResolver.java | 2 +- 8 files changed, 96 insertions(+), 58 deletions(-) rename {spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util => spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json}/GsonMapper.java (86%) rename {spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util => spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json}/JacksonMapper.java (78%) rename {spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util => spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json}/JsonMapper.java (90%) rename spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/util/MapperTests.java => spring-cloud-function-context/src/test/java/org/springframework/cloud/function/util/JsonMapperTests.java (78%) diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml index f111e1a40..badafe205 100644 --- a/spring-cloud-function-context/pom.xml +++ b/spring-cloud-function-context/pom.xml @@ -32,6 +32,16 @@ spring-boot-configuration-processor true + + com.google.code.gson + gson + true + + + com.fasterxml.jackson.core + jackson-databind + true + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java index dcf7db156..ba415f6b9 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java @@ -39,6 +39,9 @@ import java.util.function.Supplier; import javax.annotation.PreDestroy; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; @@ -46,7 +49,10 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistry; @@ -62,8 +68,11 @@ import org.springframework.cloud.function.core.FunctionFactoryMetadata; import org.springframework.cloud.function.core.IsolatedConsumer; import org.springframework.cloud.function.core.IsolatedFunction; import org.springframework.cloud.function.core.IsolatedSupplier; +import org.springframework.cloud.function.json.GsonMapper; +import org.springframework.cloud.function.json.JacksonMapper; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -86,6 +95,8 @@ import org.springframework.util.StringUtils; @ConditionalOnMissingBean(FunctionCatalog.class) public class ContextFunctionCatalogAutoConfiguration { + static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper"; + @Autowired(required = false) private Map> suppliers = Collections.emptyMap(); @@ -169,6 +180,26 @@ public class ContextFunctionCatalogAutoConfiguration { } + @Configuration + @ConditionalOnClass(Gson.class) + @Conditional(PreferGsonOrMissingJacksonCondition.class) + protected static class GsonConfiguration { + @Bean + public GsonMapper jsonMapper(Gson gson) { + return new GsonMapper(gson); + } + } + + @Configuration + @ConditionalOnClass(ObjectMapper.class) + @ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true) + protected static class JacksonConfiguration { + @Bean + public JacksonMapper jsonMapper(ObjectMapper mapper) { + return new JacksonMapper(mapper); + } + } + @Component protected static class ContextFunctionRegistry { @@ -643,4 +674,22 @@ public class ContextFunctionCatalogAutoConfiguration { } + private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition { + + PreferGsonOrMissingJacksonCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) + static class GsonPreferred { + + } + + @ConditionalOnMissingBean(ObjectMapper.class) + static class JacksonMissing { + + } + + } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/GsonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/GsonMapper.java similarity index 86% rename from spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/GsonMapper.java rename to spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/GsonMapper.java index 061ddbe11..988742094 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/GsonMapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/GsonMapper.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.function.web.util; +package org.springframework.cloud.function.json; import java.util.ArrayList; import java.util.List; import com.google.gson.Gson; +import org.springframework.cloud.function.json.JsonMapper; import org.springframework.core.ResolvableType; /** @@ -45,4 +46,8 @@ public class GsonMapper implements JsonMapper { return gson.fromJson(json, type); } + @Override + public String toString(Object value) { + return gson.toJson(value); + } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/JacksonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java similarity index 78% rename from spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/JacksonMapper.java rename to spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java index 4944dcf44..476b99719 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/JacksonMapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.function.web.util; +package org.springframework.cloud.function.json; import java.util.ArrayList; import java.util.List; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.cloud.function.json.JsonMapper; + /** * @author Dave Syer * @@ -53,4 +56,13 @@ public class JacksonMapper implements JsonMapper { } } + @Override + public String toString(Object value) { + try { + return mapper.writeValueAsString(value); + } + catch (JsonProcessingException e) { + throw new IllegalArgumentException("Cannot convert to JSON", e); + } + } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/JsonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java similarity index 90% rename from spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/JsonMapper.java rename to spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java index 7319cb2fb..41b465e7c 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/JsonMapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.function.web.util; +package org.springframework.cloud.function.json; import java.util.List; @@ -27,4 +27,6 @@ public interface JsonMapper { T toSingle(String json, Class type); + String toString(Object value); + } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/util/MapperTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/util/JsonMapperTests.java similarity index 78% rename from spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/util/MapperTests.java rename to spring-cloud-function-context/src/test/java/org/springframework/cloud/function/util/JsonMapperTests.java index cd5c21888..64a7bba6e 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/util/MapperTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/util/JsonMapperTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.function.web.util; +package org.springframework.cloud.function.util; import java.util.Arrays; import java.util.List; @@ -26,6 +26,10 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.springframework.cloud.function.json.GsonMapper; +import org.springframework.cloud.function.json.JacksonMapper; +import org.springframework.cloud.function.json.JsonMapper; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -33,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; * */ @RunWith(Parameterized.class) -public class MapperTests { +public class JsonMapperTests { private JsonMapper mapper; @@ -43,16 +47,18 @@ public class MapperTests { new Object[] { new JacksonMapper(new ObjectMapper()) }); } - public MapperTests(JsonMapper mapper) { + public JsonMapperTests(JsonMapper mapper) { this.mapper = mapper; } @Test public void vanillaArray() { - List list = mapper.toList("[{\"value\":\"foo\"}, {\"value\":\"foo\"}]", + String json = "[{\"value\":\"foo\"},{\"value\":\"foo\"}]"; + List list = mapper.toList(json, Foo.class); assertThat(list).hasSize(2); assertThat(list.get(0).getValue()).isEqualTo("foo"); + assertThat(mapper.toString(list)).isEqualTo(json); } @Test @@ -70,8 +76,10 @@ public class MapperTests { @Test public void vanillaObject() { - Foo foo = mapper.toSingle("{\"value\":\"foo\"}", Foo.class); + String json = "{\"value\":\"foo\"}"; + Foo foo = mapper.toSingle(json, Foo.class); assertThat(foo.getValue()).isEqualTo("foo"); + assertThat(mapper.toString(foo)).isEqualTo(json); } @Test diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java index 814c27806..e000ba4a1 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java @@ -19,32 +19,24 @@ package org.springframework.cloud.function.web.flux; import java.util.ArrayList; import java.util.List; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; - import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.catalog.FunctionInspector; +import org.springframework.cloud.function.json.JsonMapper; import org.springframework.cloud.function.web.flux.request.FluxHandlerMethodArgumentResolver; import org.springframework.cloud.function.web.flux.response.FluxReturnValueHandler; -import org.springframework.cloud.function.web.util.GsonMapper; -import org.springframework.cloud.function.web.util.JacksonMapper; -import org.springframework.cloud.function.web.util.JsonMapper; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.convert.ConversionService; @@ -71,8 +63,6 @@ public class ReactorAutoConfiguration { @Autowired private ApplicationContext context; - static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper"; - @Bean public FunctionHandlerMapping functionHandlerMapping(FunctionCatalog catalog, FunctionController controller) { @@ -129,44 +119,6 @@ public class ReactorAutoConfiguration { }; } - @Configuration - @ConditionalOnClass(Gson.class) - @Conditional(PreferGsonOrMissingJacksonCondition.class) - protected static class GsonConfiguration { - @Bean - public GsonMapper jsonMapper(Gson gson) { - return new GsonMapper(gson); - } - } - - @Configuration - @ConditionalOnClass(ObjectMapper.class) - @ConditionalOnProperty(name = ReactorAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true) - protected static class JacksonConfiguration { - @Bean - public JacksonMapper jsonMapper(ObjectMapper mapper) { - return new JacksonMapper(mapper); - } - } - - private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition { - - PreferGsonOrMissingJacksonCondition() { - super(ConfigurationPhase.REGISTER_BEAN); - } - - @ConditionalOnProperty(name = ReactorAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) - static class GsonPreferred { - - } - - @ConditionalOnMissingBean(ObjectMapper.class) - static class JacksonMissing { - - } - - } - private static class BasicStringConverter implements StringConverter { private ConversionService conversionService; diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/request/FluxHandlerMethodArgumentResolver.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/request/FluxHandlerMethodArgumentResolver.java index b0b18a4b6..386be5b43 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/request/FluxHandlerMethodArgumentResolver.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/request/FluxHandlerMethodArgumentResolver.java @@ -28,9 +28,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.cloud.function.context.catalog.FunctionInspector; import org.springframework.cloud.function.context.message.MessageUtils; +import org.springframework.cloud.function.json.JsonMapper; import org.springframework.cloud.function.web.flux.constants.WebRequestConstants; import org.springframework.cloud.function.web.util.HeaderUtils; -import org.springframework.cloud.function.web.util.JsonMapper; import org.springframework.core.MethodParameter; import org.springframework.core.Ordered; import org.springframework.http.MediaType;