CAMEL-7619: Rest DSL - adding support for xml/json binding using Camel's data formats.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/bca60fe0 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/bca60fe0 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/bca60fe0 Branch: refs/heads/master Commit: bca60fe04d19399db2b348c1eeb9c2fede2562db Parents: 2e6347f Author: Claus Ibsen <davscl...@apache.org> Authored: Mon Jul 28 10:47:56 2014 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Mon Jul 28 10:50:49 2014 +0200 ---------------------------------------------------------------------- .../processor/binding/RestBindingProcessor.java | 108 ++++++++++--------- .../RestRestletBindingModeAutoWithJsonTest.java | 61 +++++++++++ .../RestRestletBindingModeAutoWithXmlTest.java | 61 +++++++++++ 3 files changed, 182 insertions(+), 48 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/bca60fe0/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java b/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java index b6ecbb4..0ae78d8 100644 --- a/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java +++ b/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java @@ -43,8 +43,8 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess private final AsyncProcessor jsonUnmarshal; private final AsyncProcessor xmlUnmarshal; - private final AsyncProcessor jsonMmarshal; - private final AsyncProcessor xmlMmarshal; + private final AsyncProcessor jsonMarshal; + private final AsyncProcessor xmlMarshal; private final String consumes; private final String produces; private final String bindingMode; @@ -59,11 +59,11 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess this.jsonUnmarshal = null; } if (outJsonDataFormat != null) { - this.jsonMmarshal = new MarshalProcessor(outJsonDataFormat); + this.jsonMarshal = new MarshalProcessor(outJsonDataFormat); } else if (jsonDataFormat != null) { - this.jsonMmarshal = new MarshalProcessor(jsonDataFormat); + this.jsonMarshal = new MarshalProcessor(jsonDataFormat); } else { - this.jsonMmarshal = null; + this.jsonMarshal = null; } if (xmlDataFormat != null) { @@ -72,11 +72,11 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess this.xmlUnmarshal = null; } if (outXmlDataFormat != null) { - this.xmlMmarshal = new MarshalProcessor(outXmlDataFormat); + this.xmlMarshal = new MarshalProcessor(outXmlDataFormat); } else if (xmlDataFormat != null) { - this.xmlMmarshal = new MarshalProcessor(xmlDataFormat); + this.xmlMarshal = new MarshalProcessor(xmlDataFormat); } else { - this.xmlMmarshal = null; + this.xmlMarshal = null; } this.consumes = consumes; @@ -90,7 +90,6 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess } // TODO: consumes/produces can be a list of media types, and prioritized 1st to last. - // TODO: parsing body should only be done if really needed @Override public boolean process(Exchange exchange, final AsyncCallback callback) { @@ -128,28 +127,37 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess isJson = consumes != null && consumes.toLowerCase(Locale.US).contains("json"); } - // we have binding enabled, so we need to know if there body is empty or not\ - // so force reading the body as a String which we can work with - String body = MessageHelper.extractBodyAsString(exchange.getIn()); - if (body != null) { - exchange.getIn().setBody(body); + // only allow xml/json if the binding mode allows that + isXml &= bindingMode.equals("auto") || bindingMode.contains("xml"); + isJson &= bindingMode.equals("auto") || bindingMode.contains("json"); + + // if we do not yet know if its xml or json, then use the binding mode to know the mode + if (!isJson && !isXml) { + isXml = bindingMode.equals("auto") || bindingMode.contains("xml"); + isJson = bindingMode.equals("auto") || bindingMode.contains("json"); } - // if we do not know if its xml/json then try check if the body is xml - if (!isXml && !isJson || isXml && isJson) { + // okay we have a binding mode, so need to check for empty body as that can cause the marshaller to fail + // as they assume a non-empty body + String body = null; + if (isXml || isJson) { + // we have binding enabled, so we need to know if there body is empty or not\ + // so force reading the body as a String which we can work with + body = MessageHelper.extractBodyAsString(exchange.getIn()); if (body != null) { - isXml = body.startsWith("<"); - isJson = !isXml; + exchange.getIn().setBody(body); + + if (isXml && isJson) { + // we have still not determined between xml or json, so check the body if its xml based or not + isXml = body.startsWith("<"); + isJson = !isXml; + } } } - // only allow xml/json if the binding mode allows that - isXml &= bindingMode.equals("auto") || bindingMode.contains("xml"); - isJson &= bindingMode.equals("auto") || bindingMode.contains("json"); - - if (isXml && xmlUnmarshal != null) { + if (isXml && xmlUnmarshal != null) { // add reverse operation - exchange.addOnCompletion(new RestBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMmarshal, xmlMmarshal)); + exchange.addOnCompletion(new RestBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMarshal, xmlMarshal, true)); if (ObjectHelper.isNotEmpty(body)) { return xmlUnmarshal.process(exchange, callback); } else { @@ -158,7 +166,7 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess } } else if (isJson && jsonUnmarshal != null) { // add reverse operation - exchange.addOnCompletion(new RestBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMmarshal, xmlMmarshal)); + exchange.addOnCompletion(new RestBindingMarshalOnCompletion(exchange.getFromRouteId(), jsonMarshal, xmlMarshal, false)); if (ObjectHelper.isNotEmpty(body)) { return jsonUnmarshal.process(exchange, callback); } else { @@ -204,14 +212,16 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess */ private final class RestBindingMarshalOnCompletion extends SynchronizationAdapter { - private final AsyncProcessor jsonMmarshal; - private final AsyncProcessor xmlMmarshal; + private final AsyncProcessor jsonMarshal; + private final AsyncProcessor xmlMarshal; private final String routeId; + private boolean wasXml; - private RestBindingMarshalOnCompletion(String routeId, AsyncProcessor jsonMmarshal, AsyncProcessor xmlMmarshal) { + private RestBindingMarshalOnCompletion(String routeId, AsyncProcessor jsonMarshal, AsyncProcessor xmlMarshal, boolean wasXml) { this.routeId = routeId; - this.jsonMmarshal = jsonMmarshal; - this.xmlMmarshal = xmlMmarshal; + this.jsonMarshal = jsonMarshal; + this.xmlMarshal = xmlMarshal; + this.wasXml = wasXml; } @Override @@ -235,7 +245,7 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess } // is there any marshaller at all - if (jsonMmarshal == null && xmlMmarshal == null) { + if (jsonMarshal == null && xmlMarshal == null) { return; } @@ -259,28 +269,30 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess isJson = produces != null && produces.toLowerCase(Locale.US).contains("json"); } - // need to prepare exchange first - ExchangeHelper.prepareOutToIn(exchange); - - // if we do not know explicit if its json or xml, then need to check the message body to be sure what it is - if (!isXml && !isJson || isXml && isJson) { - // read the content into memory so we can determine if its xml or json - String body = MessageHelper.extractBodyAsString(exchange.getIn()); - if (body != null) { - isXml = body.startsWith("<"); - isJson = !isXml; - } - } - // only allow xml/json if the binding mode allows that isXml &= bindingMode.equals("auto") || bindingMode.contains("xml"); isJson &= bindingMode.equals("auto") || bindingMode.contains("json"); + // if we do not yet know if its xml or json, then use the binding mode to know the mode + if (!isJson && !isXml) { + isXml = bindingMode.equals("auto") || bindingMode.contains("xml"); + isJson = bindingMode.equals("auto") || bindingMode.contains("json"); + } + + // in case we have not yet been able to determine if xml or json, then use the same as in the unmarshaller + if (isXml && isJson) { + isXml = wasXml; + isJson = !wasXml; + } + + // need to prepare exchange first + ExchangeHelper.prepareOutToIn(exchange); + try { - if (isXml && xmlMmarshal != null) { - xmlMmarshal.process(exchange); - } else if (isJson && jsonMmarshal != null) { - jsonMmarshal.process(exchange); + if (isXml && xmlMarshal != null) { + xmlMarshal.process(exchange); + } else if (isJson && jsonMarshal != null) { + jsonMarshal.process(exchange); } else { // we could not bind if (bindingMode.equals("auto")) { http://git-wip-us.apache.org/repos/asf/camel/blob/bca60fe0/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithJsonTest.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithJsonTest.java b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithJsonTest.java new file mode 100644 index 0000000..33427c6 --- /dev/null +++ b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithJsonTest.java @@ -0,0 +1,61 @@ +/** + * 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.restlet; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.model.rest.RestBindingMode; +import org.junit.Test; + +/** + * @version + */ +public class RestRestletBindingModeAutoWithJsonTest extends RestletTestSupport { + + @Test + public void testBindingMode() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:input"); + mock.expectedMessageCount(1); + mock.message(0).body().isInstanceOf(UserPojo.class); + + String body = "{\"id\": 123, \"name\": \"Donald Duck\"}"; + template.sendBody("http://localhost:" + portNum + "/users/new", body); + + assertMockEndpointsSatisfied(); + + UserPojo user = mock.getReceivedExchanges().get(0).getIn().getBody(UserPojo.class); + assertNotNull(user); + assertEquals(123, user.getId()); + assertEquals("Donald Duck", user.getName()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + // binding mode is json only + restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.auto); + + // use the rest DSL to define the rest services + rest("/users/") + .post("new").consumes("application/json").type(UserPojo.class) + .to("mock:input"); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/bca60fe0/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithXmlTest.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithXmlTest.java b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithXmlTest.java new file mode 100644 index 0000000..29779d5 --- /dev/null +++ b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletBindingModeAutoWithXmlTest.java @@ -0,0 +1,61 @@ +/** + * 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.restlet; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.model.rest.RestBindingMode; +import org.junit.Test; + +/** + * @version + */ +public class RestRestletBindingModeAutoWithXmlTest extends RestletTestSupport { + + @Test + public void testBindingMode() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:input"); + mock.expectedMessageCount(1); + mock.message(0).body().isInstanceOf(UserJaxbPojo.class); + + String body = "<user name=\"Donald Duck\" id=\"123\"></user>"; + template.sendBody("http://localhost:" + portNum + "/users/new", body); + + assertMockEndpointsSatisfied(); + + UserJaxbPojo user = mock.getReceivedExchanges().get(0).getIn().getBody(UserJaxbPojo.class); + assertNotNull(user); + assertEquals(123, user.getId()); + assertEquals("Donald Duck", user.getName()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + // binding mode is json only + restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.auto); + + // use the rest DSL to define the rest services + rest("/users/") + .post("new").consumes("application/xml").type(UserJaxbPojo.class) + .to("mock:input"); + } + }; + } +}