CAMEL-9583: camel-jackson - Allow the FallbackTypeConverter to convert to/from string types
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/2b4b9df6 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/2b4b9df6 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/2b4b9df6 Branch: refs/heads/master Commit: 2b4b9df67da4b84cb69d9dd42ef77155a803679a Parents: 7009d64 Author: Claus Ibsen <davscl...@apache.org> Authored: Wed Feb 10 09:43:46 2016 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Wed Feb 10 09:44:40 2016 +0100 ---------------------------------------------------------------------- .../component/jackson/JacksonConstants.java | 2 +- .../converter/JacksonTypeConverters.java | 62 +++++++++++++---- .../jackson/JacksonConversionsSimpleTest.java | 69 ------------------- .../jackson/JacksonConversionsTest.java | 58 ---------------- .../converter/JacksonConversionsPojoTest.java | 58 ++++++++++++++++ .../converter/JacksonConversionsSimpleTest.java | 70 ++++++++++++++++++++ .../converter/JacksonConversionsTest.java | 60 +++++++++++++++++ .../component/jackson/converter/Order.java | 68 +++++++++++++++++++ 8 files changed, 306 insertions(+), 141 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonConstants.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonConstants.java b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonConstants.java index d4f5c26..07f6236 100644 --- a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonConstants.java +++ b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonConstants.java @@ -19,7 +19,7 @@ package org.apache.camel.component.jackson; public final class JacksonConstants { public static final String ENABLE_TYPE_CONVERTER = "CamelJacksonEnableTypeConverter"; - public static final String TYPE_CONVERTER_POJO_ONLY = "CamelJacksonTypeConverterPojoOnly"; + public static final String TYPE_CONVERTER_TO_POJO = "CamelJacksonTypeConverterToPojo"; public static final String UNMARSHAL_TYPE = "CamelJacksonUnmarshalType"; http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/converter/JacksonTypeConverters.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/converter/JacksonTypeConverters.java b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/converter/JacksonTypeConverters.java index 110b6ce..567af5c 100644 --- a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/converter/JacksonTypeConverters.java +++ b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/converter/JacksonTypeConverters.java @@ -16,26 +16,42 @@ */ package org.apache.camel.component.jackson.converter; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; import java.util.Set; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; +import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.FallbackConverter; import org.apache.camel.component.jackson.JacksonConstants; import org.apache.camel.spi.Registry; import org.apache.camel.spi.TypeConverterRegistry; +/** + * Jackson {@link org.apache.camel.TypeConverter} that allows converting json to/from POJOs and other types. + * <br/> + * This implementation uses a {@link FallbackConverter}. + * <p/> + * The converter is disabled by default. To enable then set the property + * {@link JacksonConstants#ENABLE_TYPE_CONVERTER} to <tt>true</tt> on {@link CamelContext#getProperties()}. + * <br/> + * The option {@link JacksonConstants#TYPE_CONVERTER_TO_POJO} can be used to allow converting to POJO types. By + * default the converter only attempts to convert to primitive types such as String and numbers. To convert to any kind, then + * enable this by setting {@link JacksonConstants#TYPE_CONVERTER_TO_POJO} to <tt>true</tt> on {@link CamelContext#getProperties()}. + */ public final class JacksonTypeConverters { private final ObjectMapper defaultMapper; private boolean init; - private Boolean enabled; - private boolean pojoOnly = true; + private boolean enabled; + private boolean toPojo; public JacksonTypeConverters() { defaultMapper = new ObjectMapper(); - // Enables JAXB processing + // Enables JAXB processing so we can easily convert JAXB annotated pojos also JaxbAnnotationModule module = new JaxbAnnotationModule(); defaultMapper.registerModule(module); } @@ -43,31 +59,39 @@ public final class JacksonTypeConverters { @FallbackConverter public <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry) throws Exception { - // only do this if enabled + // only do this if enabled (disabled by default) if (!init && exchange != null) { // init to see if this is enabled String text = exchange.getContext().getProperties().get(JacksonConstants.ENABLE_TYPE_CONVERTER); - enabled = "true".equalsIgnoreCase(text); + if (text != null) { + text = exchange.getContext().resolvePropertyPlaceholders(text); + enabled = "true".equalsIgnoreCase(text); + } - // pojo only by default - text = exchange.getContext().getProperties().get(JacksonConstants.TYPE_CONVERTER_POJO_ONLY); + // pojoOnly is enabled by default + text = exchange.getContext().getProperties().get(JacksonConstants.TYPE_CONVERTER_TO_POJO); if (text != null) { - pojoOnly = "true".equalsIgnoreCase(text); + text = exchange.getContext().resolvePropertyPlaceholders(text); + toPojo = "true".equalsIgnoreCase(text); } init = true; } - if (enabled == null || !enabled) { + if (!enabled) { return null; } - if (pojoOnly && isNotPojoType(type)) { + if (!toPojo && isNotPojoType(type)) { return null; } if (exchange != null) { ObjectMapper mapper = resolveObjectMapper(exchange.getContext().getRegistry()); + + // favor use write/read operations as they are higher level than the convertValue + + // if we want to convert to a String or byte[] then use write operation if (String.class.isAssignableFrom(type)) { String out = mapper.writeValueAsString(value); return type.cast(out); @@ -75,12 +99,21 @@ public final class JacksonTypeConverters { byte[] out = mapper.writeValueAsBytes(value); return type.cast(out); } else if (mapper.canSerialize(type)) { - // TODO: favor using mapper readValue - // if the input is string or input stream + // if the source value type is readable by the mapper then use its read operation if (String.class.isAssignableFrom(value.getClass())) { return mapper.readValue((String) value, type); + } else if (byte[].class.isAssignableFrom(value.getClass())) { + return mapper.readValue((byte[]) value, type); + } else if (File.class.isAssignableFrom(value.getClass())) { + return mapper.readValue((File) value, type); + } else if (InputStream.class.isAssignableFrom(value.getClass())) { + return mapper.readValue((InputStream) value, type); + } else if (Reader.class.isAssignableFrom(value.getClass())) { + return mapper.readValue((Reader) value, type); + } else { + // fallback to generic convert value + return mapper.convertValue(value, type); } - return mapper.convertValue(value, type); } } @@ -88,6 +121,9 @@ public final class JacksonTypeConverters { return null; } + /** + * Whether the type is NOT a pojo type but only a set of simple types such as String and numbers. + */ private static boolean isNotPojoType(Class<?> type) { boolean isString = String.class.isAssignableFrom(type); boolean isNumber = Number.class.isAssignableFrom(type) http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsSimpleTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsSimpleTest.java b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsSimpleTest.java deleted file mode 100644 index 1128fa4..0000000 --- a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsSimpleTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.camel.component.jackson; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.camel.CamelContext; -import org.apache.camel.Exchange; -import org.apache.camel.impl.DefaultExchange; -import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.Test; - -public class JacksonConversionsSimpleTest extends CamelTestSupport { - - @Override - public boolean isUseRouteBuilder() { - return false; - } - - @Override - protected CamelContext createCamelContext() throws Exception { - CamelContext context = super.createCamelContext(); - // enable jackson type converter by setting this property on CamelContext - context.getProperties().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); - return context; - } - - @Test - public void shouldNotConvertMapToString() { - Exchange exchange = new DefaultExchange(context); - - Map<String, String> body = new HashMap<String, String>(); - Object convertedObject = context.getTypeConverter().convertTo(String.class, exchange, body); - // will do a toString which is an empty map - assertEquals(body.toString(), convertedObject); - } - - @Test - public void shouldNotConvertMapToNumber() { - Exchange exchange = new DefaultExchange(context); - - Object convertedObject = context.getTypeConverter().convertTo(Long.class, exchange, new HashMap<String, String>()); - assertNull(convertedObject); - } - - @Test - public void shouldNotConvertMapToPrimitive() { - Exchange exchange = new DefaultExchange(context); - Object convertedObject = context.getTypeConverter().convertTo(long.class, exchange, new HashMap<String, String>()); - - assertNull(convertedObject); - } - -} http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsTest.java b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsTest.java deleted file mode 100644 index 255e033..0000000 --- a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/JacksonConversionsTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.camel.component.jackson; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.camel.CamelContext; -import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.Test; - -public class JacksonConversionsTest extends CamelTestSupport { - - @Override - protected CamelContext createCamelContext() throws Exception { - CamelContext context = super.createCamelContext(); - // enable jackson type converter by setting this property on CamelContext - context.getProperties().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); - return context; - } - - @Test - public void shouldConvertMapToPojo() { - String name = "someName"; - Map<String, String> pojoAsMap = new HashMap<String, String>(); - pojoAsMap.put("name", name); - - TestPojo testPojo = (TestPojo) template.requestBody("direct:test", pojoAsMap); - - assertEquals(name, testPojo.getName()); - } - - @Override - protected RouteBuilder createRouteBuilder() throws Exception { - return new RouteBuilder() { - @Override - public void configure() throws Exception { - from("direct:test").convertBodyTo(TestPojo.class); - } - }; - } - -} http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsPojoTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsPojoTest.java b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsPojoTest.java new file mode 100644 index 0000000..4df73e6 --- /dev/null +++ b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsPojoTest.java @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.jackson.converter; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jackson.JacksonConstants; +import org.apache.camel.component.jackson.converter.Order; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class JacksonConversionsPojoTest extends CamelTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + // enable jackson type converter by setting this property on CamelContext + context.getProperties().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); + context.getProperties().put(JacksonConstants.TYPE_CONVERTER_TO_POJO, "true"); + return context; + } + + @Test + public void shouldConvertPojoToString() { + Order order = new Order(); + order.setAmount(1); + order.setCustomerName("Acme"); + order.setPartName("Camel"); + + String json = (String) template.requestBody("direct:test", order); + assertEquals("{\"id\":0,\"partName\":\"Camel\",\"amount\":1,\"customerName\":\"Acme\"}", json); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:test").convertBodyTo(String.class); + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsSimpleTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsSimpleTest.java b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsSimpleTest.java new file mode 100644 index 0000000..3f05880 --- /dev/null +++ b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsSimpleTest.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.jackson.converter; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.component.jackson.JacksonConstants; +import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class JacksonConversionsSimpleTest extends CamelTestSupport { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + // enable jackson type converter by setting this property on CamelContext + context.getProperties().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); + return context; + } + + @Test + public void shouldNotConvertMapToString() { + Exchange exchange = new DefaultExchange(context); + + Map<String, String> body = new HashMap<String, String>(); + Object convertedObject = context.getTypeConverter().convertTo(String.class, exchange, body); + // will do a toString which is an empty map + assertEquals(body.toString(), convertedObject); + } + + @Test + public void shouldNotConvertMapToNumber() { + Exchange exchange = new DefaultExchange(context); + + Object convertedObject = context.getTypeConverter().convertTo(Long.class, exchange, new HashMap<String, String>()); + assertNull(convertedObject); + } + + @Test + public void shouldNotConvertMapToPrimitive() { + Exchange exchange = new DefaultExchange(context); + Object convertedObject = context.getTypeConverter().convertTo(long.class, exchange, new HashMap<String, String>()); + + assertNull(convertedObject); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsTest.java b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsTest.java new file mode 100644 index 0000000..83a2c9d --- /dev/null +++ b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/JacksonConversionsTest.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.jackson.converter; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jackson.JacksonConstants; +import org.apache.camel.component.jackson.TestPojo; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class JacksonConversionsTest extends CamelTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + // enable jackson type converter by setting this property on CamelContext + context.getProperties().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); + return context; + } + + @Test + public void shouldConvertMapToPojo() { + String name = "someName"; + Map<String, String> pojoAsMap = new HashMap<String, String>(); + pojoAsMap.put("name", name); + + TestPojo testPojo = (TestPojo) template.requestBody("direct:test", pojoAsMap); + + assertEquals(name, testPojo.getName()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:test").convertBodyTo(TestPojo.class); + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/2b4b9df6/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/Order.java ---------------------------------------------------------------------- diff --git a/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/Order.java b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/Order.java new file mode 100644 index 0000000..6202d3c --- /dev/null +++ b/components/camel-jackson/src/test/java/org/apache/camel/component/jackson/converter/Order.java @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.jackson.converter; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "order") +@XmlAccessorType(XmlAccessType.FIELD) +public class Order { + + @XmlAttribute + private int id; + @XmlAttribute + private String partName; + @XmlAttribute + private int amount; + @XmlAttribute + private String customerName; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getPartName() { + return partName; + } + + public void setPartName(String partName) { + this.partName = partName; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } +}