You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2016/12/23 12:14:23 UTC
[2/4] camel git commit: CAMEL-10646: jsonpath now supports POJOs if
jackson is on the classpath
CAMEL-10646: jsonpath now supports POJOs if jackson is on the classpath
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/0a3efe21
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/0a3efe21
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/0a3efe21
Branch: refs/heads/master
Commit: 0a3efe214aa6bb6b6e6c304444595f0ae30c72b3
Parents: 2baea25
Author: Claus Ibsen <da...@apache.org>
Authored: Fri Dec 23 12:54:24 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Fri Dec 23 12:54:24 2016 +0100
----------------------------------------------------------------------
components/camel-jsonpath/pom.xml | 20 +++-
.../apache/camel/jsonpath/JsonPathAdapter.java | 33 +++++++
.../apache/camel/jsonpath/JsonPathEngine.java | 97 ++++++++++++++++++--
.../jsonpath/jackson/JacksonJsonAdapter.java | 64 +++++++++++++
.../jsonpath/JsonPathPojoTransformTest.java | 51 ++++++++++
.../org/apache/camel/jsonpath/MyPojoType.java | 39 ++++++++
6 files changed, 295 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/0a3efe21/components/camel-jsonpath/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/pom.xml b/components/camel-jsonpath/pom.xml
index 499e99c..b20deb3 100644
--- a/components/camel-jsonpath/pom.xml
+++ b/components/camel-jsonpath/pom.xml
@@ -31,8 +31,16 @@
<description>Camel JSON Path Language</description>
<properties>
- <camel.osgi.export.pkg>org.apache.camel.jsonpath.*</camel.osgi.export.pkg>
+ <camel.osgi.export.pkg>
+ org.apache.camel.jsonpath,
+ !org.apache.camel.jsonpath.jackson
+ </camel.osgi.export.pkg>
<camel.osgi.export.service>org.apache.camel.spi.LanguageResolver;language=jsonpath</camel.osgi.export.service>
+ <camel.osgi.import>
+ com.fasterxml.jackson.databind;resolution:=optional,
+ com.fasterxml.jackson.module.jaxb;resolution:=optional,
+ *
+ </camel.osgi.import>
</properties>
<dependencies>
@@ -50,6 +58,16 @@
<artifactId>json-smart</artifactId>
<version>${json-smart-version}</version>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-jaxb-annotations</artifactId>
+ <optional>true</optional>
+ </dependency>
<!-- testing -->
<dependency>
http://git-wip-us.apache.org/repos/asf/camel/blob/0a3efe21/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAdapter.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAdapter.java
new file mode 100644
index 0000000..41e0903
--- /dev/null
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * 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.jsonpath;
+
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+
+public interface JsonPathAdapter {
+
+ /**
+ * Attempt to read/convert the message body into a {@link Map} type
+ *
+ * @param body the message body
+ * @param exchange the Camel exchange
+ * @return converted as {@link Map} or <tt>null</tt> if not possible
+ */
+ Map readValue(Object body, Exchange exchange);
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/0a3efe21/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
index 14071d7..0816e30 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
@@ -30,22 +31,29 @@ import com.jayway.jsonpath.Configuration.Defaults;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.internal.DefaultsImpl;
-
+import org.apache.camel.CamelExchangeException;
import org.apache.camel.Exchange;
import org.apache.camel.Expression;
-import org.apache.camel.InvalidPayloadException;
import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static com.jayway.jsonpath.Option.ALWAYS_RETURN_LIST;
+import static com.jayway.jsonpath.Option.SUPPRESS_EXCEPTIONS;
+
public class JsonPathEngine {
private static final Logger LOG = LoggerFactory.getLogger(JsonPathEngine.class);
+ private static final String JACKSON_JSON_ADAPTER = "org.apache.camel.jsonpath.jackson.JacksonJsonAdapter";
+
private static final Pattern SIMPLE_PATTERN = Pattern.compile("\\$\\{[^\\}]+\\}", Pattern.MULTILINE);
private final String expression;
private final JsonPath path;
private final Configuration configuration;
+ private JsonPathAdapter adapter;
+ private volatile boolean initJsonAdapter;
public JsonPathEngine(String expression) {
this(expression, false, true, null);
@@ -58,13 +66,13 @@ public class JsonPathEngine {
if (options != null) {
Configuration.ConfigurationBuilder builder = Configuration.builder().jsonProvider(defaults.jsonProvider()).options(options);
if (suppressExceptions) {
- builder.options(Option.SUPPRESS_EXCEPTIONS);
+ builder.options(SUPPRESS_EXCEPTIONS);
}
this.configuration = builder.build();
} else {
Configuration.ConfigurationBuilder builder = Configuration.builder().jsonProvider(defaults.jsonProvider());
if (suppressExceptions) {
- builder.options(Option.SUPPRESS_EXCEPTIONS);
+ builder.options(SUPPRESS_EXCEPTIONS);
}
this.configuration = builder.build();
}
@@ -85,7 +93,7 @@ public class JsonPathEngine {
}
}
- public Object read(Exchange exchange) throws IOException, InvalidPayloadException {
+ public Object read(Exchange exchange) throws Exception {
if (path == null) {
Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
String text = exp.evaluate(exchange, String.class);
@@ -97,10 +105,13 @@ public class JsonPathEngine {
}
}
- private Object doRead(JsonPath path, Exchange exchange) throws IOException, InvalidPayloadException {
+ private Object doRead(JsonPath path, Exchange exchange) throws IOException, CamelExchangeException {
Object json = exchange.getIn().getBody();
- if (json instanceof GenericFile) {
+ if (json instanceof InputStream) {
+ return readWithInputStream(path, exchange);
+ } else if (json instanceof GenericFile) {
+ LOG.trace("JSonPath: {} is read as generic file from message body: {}", path, json);
GenericFile<?> genericFile = (GenericFile<?>) json;
if (genericFile.getCharset() != null) {
// special treatment for generic file with charset
@@ -110,16 +121,48 @@ public class JsonPathEngine {
}
if (json instanceof String) {
+ LOG.trace("JSonPath: {} is read as String from message body: {}", path, json);
String str = (String) json;
return path.read(str, configuration);
} else if (json instanceof Map) {
+ LOG.trace("JSonPath: {} is read as Map from message body: {}", path, json);
Map map = (Map) json;
return path.read(map, configuration);
} else if (json instanceof List) {
+ LOG.trace("JSonPath: {} is read as List from message body: {}", path, json);
List list = (List) json;
return path.read(list, configuration);
} else {
- InputStream is = exchange.getIn().getMandatoryBody(InputStream.class);
+ // can we find an adapter which can read the message body
+ Object answer = readWithAdapter(path, exchange);
+ if (answer == null) {
+ // fallback and attempt input stream for any other types
+ answer = readWithInputStream(path, exchange);
+ }
+ if (answer != null) {
+ return answer;
+ }
+ }
+
+ // is json path configured to suppress exceptions
+ if (configuration.getOptions().contains(SUPPRESS_EXCEPTIONS)) {
+ if (configuration.getOptions().contains(ALWAYS_RETURN_LIST)) {
+ return Collections.emptyList();
+ } else {
+ return null;
+ }
+ }
+
+ // okay it was not then lets throw a failure
+ throw new CamelExchangeException("Cannot read message body as supported JSon value", exchange);
+ }
+
+ private Object readWithInputStream(JsonPath path, Exchange exchange) throws IOException {
+ Object json = exchange.getIn().getBody();
+ LOG.trace("JSonPath: {} is read as InputStream from message body: {}", path, json);
+
+ InputStream is = exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, exchange, json);
+ if (is != null) {
String jsonEncoding = exchange.getIn().getHeader(JsonPathConstants.HEADER_JSON_ENCODING, String.class);
if (jsonEncoding != null) {
// json encoding specified in header
@@ -131,5 +174,43 @@ public class JsonPathEngine {
return path.read(jsonStream, jsonStream.getEncoding().name(), configuration);
}
}
+
+ return null;
+ }
+
+ private Object readWithAdapter(JsonPath path, Exchange exchange) {
+ Object json = exchange.getIn().getBody();
+ LOG.trace("JSonPath: {} is read with adapter from message body: {}", path, json);
+
+ if (!initJsonAdapter) {
+ try {
+ // need to load this adapter dynamically as its optional
+ LOG.debug("Attempting to enable JacksonJsonAdapter by resolving: {} from classpath", JACKSON_JSON_ADAPTER);
+ Class<?> clazz = exchange.getContext().getClassResolver().resolveClass(JACKSON_JSON_ADAPTER);
+ if (clazz != null) {
+ Object obj = exchange.getContext().getInjector().newInstance(clazz);
+ if (obj instanceof JsonPathAdapter) {
+ adapter = (JsonPathAdapter) obj;
+ LOG.debug("JacksonJsonAdapter found on classpath and enabled for camel-jsonpath: {}", adapter);
+ }
+ }
+ } catch (Throwable e) {
+ // ignore
+ }
+ initJsonAdapter = true;
+ }
+
+ if (adapter != null) {
+ LOG.trace("Attempting to use JacksonJsonAdapter: {}", adapter);
+ Map map = adapter.readValue(json, exchange);
+ if (map != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JacksonJsonAdapter converted message body from: {} to: java.util.Map", ObjectHelper.classCanonicalName(json));
+ }
+ return path.read(map, configuration);
+ }
+ }
+
+ return null;
}
}
http://git-wip-us.apache.org/repos/asf/camel/blob/0a3efe21/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/jackson/JacksonJsonAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/jackson/JacksonJsonAdapter.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/jackson/JacksonJsonAdapter.java
new file mode 100644
index 0000000..f109628
--- /dev/null
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/jackson/JacksonJsonAdapter.java
@@ -0,0 +1,64 @@
+/**
+ * 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.jsonpath.jackson;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
+import org.apache.camel.Exchange;
+import org.apache.camel.jsonpath.JsonPathAdapter;
+import org.apache.camel.spi.Registry;
+
+/**
+ * A Jackson {@link JsonPathAdapter} which is using Jackson to convert the message
+ * body to {@link Map}. This allows us to support POJO classes with camel-jsonpath.
+ */
+public class JacksonJsonAdapter implements JsonPathAdapter {
+
+ private final ObjectMapper defaultMapper;
+
+ public JacksonJsonAdapter() {
+ defaultMapper = new ObjectMapper();
+ // Enables JAXB processing so we can easily convert JAXB annotated pojos also
+ JaxbAnnotationModule module = new JaxbAnnotationModule();
+ defaultMapper.registerModule(module);
+ }
+
+ @Override
+ public Map readValue(Object body, Exchange exchange) {
+ ObjectMapper mapper = resolveObjectMapper(exchange.getContext().getRegistry());
+ try {
+ return mapper.convertValue(body, Map.class);
+ } catch (Throwable e) {
+ // ignore because we are attempting to convert
+ }
+
+ return null;
+ }
+
+ private ObjectMapper resolveObjectMapper(Registry registry) {
+ Set<ObjectMapper> mappers = registry.findByType(ObjectMapper.class);
+ if (mappers.size() == 1) {
+ return mappers.iterator().next();
+ }
+ return defaultMapper;
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/camel/blob/0a3efe21/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathPojoTransformTest.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathPojoTransformTest.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathPojoTransformTest.java
new file mode 100644
index 0000000..209bff2
--- /dev/null
+++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathPojoTransformTest.java
@@ -0,0 +1,51 @@
+/**
+ * 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.jsonpath;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JsonPathPojoTransformTest extends CamelTestSupport {
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .transform().jsonpath("$.type")
+ .to("mock:type");
+ }
+ };
+ }
+
+ @Test
+ public void testPojo() throws Exception {
+ getMockEndpoint("mock:type").expectedBodiesReceived("customer");
+
+ // this will be read using jackson if its on the classpath
+ MyPojoType pojo = new MyPojoType();
+ pojo.setKind("full");
+ pojo.setType("customer");
+
+ template.sendBody("direct:start", pojo);
+
+ assertMockEndpointsSatisfied();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/0a3efe21/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/MyPojoType.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/MyPojoType.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/MyPojoType.java
new file mode 100644
index 0000000..243e648
--- /dev/null
+++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/MyPojoType.java
@@ -0,0 +1,39 @@
+/**
+ * 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.jsonpath;
+
+public class MyPojoType {
+
+ private String kind;
+ private String type;
+
+ public String getKind() {
+ return kind;
+ }
+
+ public void setKind(String kind) {
+ this.kind = kind;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+}