Repository: camel Updated Branches: refs/heads/master 4cca8a864 -> cae408e0d
CAMEL-7619: Rest DSL - adding support for xml/json binding using Camel's data formats. Work in progress. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/2d38cd30 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/2d38cd30 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/2d38cd30 Branch: refs/heads/master Commit: 2d38cd3044681d8c78577933b1b13cc60c8e32c8 Parents: 4cca8a8 Author: Claus Ibsen <davscl...@apache.org> Authored: Fri Jul 25 10:41:28 2014 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Fri Jul 25 11:20:54 2014 +0200 ---------------------------------------------------------------------- .../camel/model/rest/RestBindingDefinition.java | 83 ++++++++++++++++++-- .../apache/camel/model/rest/RestDefinition.java | 50 ++++++++++++ .../apache/camel/model/rest/VerbDefinition.java | 48 +++++++++++ .../processor/binding/RestBindingProcessor.java | 47 +++++++++-- .../component/restlet/RestletComponent.java | 11 +++ .../restlet/RestRestletPojoInOutTest.java | 2 +- .../restlet/SpringRestRestletPojoInOutTest.java | 53 +++++++++++++ .../restlet/SpringRestRestletPojoInOutTest.xml | 46 +++++++++++ 8 files changed, 328 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java b/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java index 229cf6f..5ffb068 100644 --- a/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/rest/RestBindingDefinition.java @@ -26,7 +26,6 @@ import javax.xml.bind.annotation.XmlTransient; import org.apache.camel.CamelContext; import org.apache.camel.Processor; import org.apache.camel.model.NoOutputDefinition; -import org.apache.camel.model.ProcessorDefinition; import org.apache.camel.processor.binding.RestBindingProcessor; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.RouteContext; @@ -64,6 +63,18 @@ public class RestBindingDefinition extends NoOutputDefinition { @XmlTransient private boolean useList; + @XmlAttribute + private String outType; + + @XmlAttribute + private String outTypeList; + + @XmlTransient + private Class<?> outClassType; + + @XmlTransient + private boolean outUseList; + @Override public String toString() { return "RestBinding"; @@ -85,14 +96,12 @@ public class RestBindingDefinition extends NoOutputDefinition { CamelContext context = routeContext.getCamelContext(); - // the default binding mode can be overriden per rest verb + // the default binding mode can be overridden per rest verb String mode = context.getRestConfiguration().getBindingMode().name(); if (bindingMode != null) { mode = bindingMode.name(); } - // auto, off, json, xml, json_xml - // setup json data format String name = jsonDataFormat; if (name == null) { @@ -116,6 +125,19 @@ public class RestBindingDefinition extends NoOutputDefinition { } context.addService(json); + DataFormat outJson = context.resolveDataFormat(name); + if (outClassType == null && outType != null) { + outClassType = context.getClassResolver().resolveMandatoryClass(outType); + } + if (outClassType == null && outTypeList != null) { + outClassType = context.getClassResolver().resolveMandatoryClass(outTypeList); + } + if (outClassType != null) { + IntrospectionSupport.setProperty(context.getTypeConverter(), outJson, "unmarshalType", outClassType); + IntrospectionSupport.setProperty(context.getTypeConverter(), outJson, "useList", outUseList); + } + context.addService(outJson); + // setup xml data format name = xmlDataFormat; if (name == null) { @@ -138,7 +160,24 @@ public class RestBindingDefinition extends NoOutputDefinition { } context.addService(jaxb); - return new RestBindingProcessor(json, jaxb, consumes, produces, mode); + DataFormat outJaxb = context.resolveDataFormat(name); + if (outClassType == null && outType != null) { + outClassType = context.getClassResolver().resolveMandatoryClass(outType); + } + if (outClassType == null && outTypeList != null) { + outClassType = context.getClassResolver().resolveMandatoryClass(outTypeList); + } + if (outClassType != null) { + JAXBContext jc = JAXBContext.newInstance(outClassType); + IntrospectionSupport.setProperty(context.getTypeConverter(), outJaxb, "context", jc); + } else if (classType != null) { + // fallback and use the context from the input + JAXBContext jc = JAXBContext.newInstance(classType); + IntrospectionSupport.setProperty(context.getTypeConverter(), outJaxb, "context", jc); + } + context.addService(outJaxb); + + return new RestBindingProcessor(json, jaxb, outJson, outJaxb, consumes, produces, mode); } public String getConsumes() { @@ -215,4 +254,38 @@ public class RestBindingDefinition extends NoOutputDefinition { this.useList = useList; } + public String getOutType() { + return outType; + } + + public void setOutType(String type) { + this.outType = type; + this.outUseList = false; + } + + public String getOutTypeList() { + return outTypeList; + } + + public void setOutTypeList(String typeList) { + this.outTypeList = typeList; + this.outUseList = true; + } + + public Class<?> getOutClassType() { + return outClassType; + } + + public void setOutClassType(Class<?> classType) { + this.outClassType = classType; + } + + public boolean isOutUseList() { + return outUseList; + } + + public void setOutUseList(boolean useList) { + this.outUseList = useList; + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java b/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java index 52947a5..d81a564 100644 --- a/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java @@ -189,6 +189,52 @@ public class RestDefinition { return this; } + public RestDefinition outType(String classType) { + // add to last verb + if (getVerbs().isEmpty()) { + throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); + } + + VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); + verb.setOutType(classType); + return this; + } + + public RestDefinition outType(Class<?> classType) { + // add to last verb + if (getVerbs().isEmpty()) { + throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); + } + + VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); + verb.setOutClassType(classType); + verb.setOutType(classType.getCanonicalName()); + return this; + } + + public RestDefinition outTypeList(String classType) { + // add to last verb + if (getVerbs().isEmpty()) { + throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); + } + + VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); + verb.setOutTypeList(classType); + return this; + } + + public RestDefinition outTypeList(Class<?> classType) { + // add to last verb + if (getVerbs().isEmpty()) { + throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); + } + + VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); + verb.setOutClassType(classType); + verb.setOutTypeList(classType.getCanonicalName()); + return this; + } + public RestDefinition bindingMode(RestBindingMode mode) { // add to last verb if (getVerbs().isEmpty()) { @@ -298,6 +344,10 @@ public class RestDefinition { binding.setTypeList(verb.getTypeList()); binding.setClassType(verb.getClassType()); binding.setUseList(verb.isUseList()); + binding.setOutType(verb.getOutType()); + binding.setOutTypeList(verb.getOutTypeList()); + binding.setOutClassType(verb.getOutClassType()); + binding.setOutUseList(verb.isOutUseList()); binding.setConsumes(verb.getConsumes()); binding.setProduces(verb.getProduces()); binding.setBindingMode(verb.getBindingMode()); http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/camel-core/src/main/java/org/apache/camel/model/rest/VerbDefinition.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/model/rest/VerbDefinition.java b/camel-core/src/main/java/org/apache/camel/model/rest/VerbDefinition.java index c2bc7fd..1032772 100644 --- a/camel-core/src/main/java/org/apache/camel/model/rest/VerbDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/rest/VerbDefinition.java @@ -53,12 +53,24 @@ public class VerbDefinition { @XmlAttribute private String typeList; + @XmlAttribute + private String outType; + + @XmlAttribute + private String outTypeList; + @XmlTransient private Class<?> classType; @XmlTransient + private Class<?> outClassType; + + @XmlTransient private boolean useList; + @XmlTransient + private boolean outUseList; + // used by XML DSL to either select a <to> or <route> // so we need to use the common type OptionalIdentifiedDefinition @XmlElements({ @@ -151,6 +163,42 @@ public class VerbDefinition { return useList; } + public String getOutType() { + return outType; + } + + public void setOutType(String type) { + this.outType = type; + this.outUseList = false; + } + + public String getOutTypeList() { + return outTypeList; + } + + public void setOutTypeList(String typeList) { + this.outTypeList = typeList; + this.outUseList = true; + } + + public Class<?> getOutClassType() { + return outClassType; + } + + public void setOutClassType(Class<?> classType) { + this.outClassType = classType; + this.outUseList = false; + } + + public void setOutListClassType(Class<?> classType) { + this.outClassType = classType; + this.outUseList = true; + } + + public boolean isOutUseList() { + return outUseList; + } + public RestDefinition getRest() { return rest; } http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/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 630210b..7560348 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 @@ -47,11 +47,36 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess private final String produces; private final String bindingMode; - public RestBindingProcessor(DataFormat jsonDataFormat, DataFormat xmlDataFormat, String consumes, String produces, String bindingMode) { - this.jsonUnmarshal = jsonDataFormat != null ? new UnmarshalProcessor(jsonDataFormat) : null; - this.jsonMmarshal = jsonDataFormat != null ? new MarshalProcessor(jsonDataFormat) : null; - this.xmlUnmarshal = xmlDataFormat != null ? new UnmarshalProcessor(xmlDataFormat) : null; - this.xmlMmarshal = xmlDataFormat != null ? new MarshalProcessor(xmlDataFormat) : null; + public RestBindingProcessor(DataFormat jsonDataFormat, DataFormat xmlDataFormat, + DataFormat outJsonDataFormat, DataFormat outXmlDataFormat, + String consumes, String produces, String bindingMode) { + + if (jsonDataFormat != null) { + this.jsonUnmarshal = new UnmarshalProcessor(jsonDataFormat); + } else { + this.jsonUnmarshal = null; + } + if (outJsonDataFormat != null) { + this.jsonMmarshal = new MarshalProcessor(outJsonDataFormat); + } else if (jsonDataFormat != null) { + this.jsonMmarshal = new MarshalProcessor(jsonDataFormat); + } else { + this.jsonMmarshal = null; + } + + if (xmlDataFormat != null) { + this.xmlUnmarshal = new UnmarshalProcessor(xmlDataFormat); + } else { + this.xmlUnmarshal = null; + } + if (outXmlDataFormat != null) { + this.xmlMmarshal = new MarshalProcessor(outXmlDataFormat); + } else if (xmlDataFormat != null) { + this.xmlMmarshal = new MarshalProcessor(xmlDataFormat); + } else { + this.xmlMmarshal = null; + } + this.consumes = consumes; this.produces = produces; this.bindingMode = bindingMode; @@ -163,7 +188,17 @@ public class RestBindingProcessor extends ServiceSupport implements AsyncProcess @Override public void onComplete(Exchange exchange) { - // only marshal if we succeeded + // only marshal if we succeeded (= onComplete) + + if (bindingMode == null || "off".equals(bindingMode)) { + // binding is off + return; + } + + // is there any marshaller at all + if (jsonMmarshal == null && xmlMmarshal == null) { + return; + } boolean isXml = false; boolean isJson = false; http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java index 4e50ea8..8707160 100644 --- a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java +++ b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java @@ -74,6 +74,7 @@ public class RestletComponent extends HeaderFilterStrategyComponent implements R private Boolean useForwardedForHeader; private Boolean reuseAddress; private boolean disableStreamCache; + private int port; public RestletComponent() { this(new Component()); @@ -108,6 +109,8 @@ public class RestletComponent extends HeaderFilterStrategyComponent implements R String host = u.getHost(); if (u.getPort() > 0) { port = u.getPort(); + } else { + port = this.port; } result.setProtocol(protocol); @@ -489,6 +492,14 @@ public class RestletComponent extends HeaderFilterStrategyComponent implements R this.disableStreamCache = disableStreamCache; } + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + @Override public Consumer createConsumer(CamelContext camelContext, Processor processor, String verb, String path, String consumes, Map<String, Object> parameters) throws Exception { http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletPojoInOutTest.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletPojoInOutTest.java b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletPojoInOutTest.java index bb9e8ba..824e4e1 100644 --- a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletPojoInOutTest.java +++ b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletPojoInOutTest.java @@ -43,7 +43,7 @@ public class RestRestletPojoInOutTest extends RestletTestSupport { // use the rest DSL to define the rest services rest("/users/") - .post("lives").type(UserPojo.class) + .post("lives").type(UserPojo.class).outType(CountryPojo.class) .route() .bean(new UserService(), "livesWhere"); } http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.java b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.java new file mode 100644 index 0000000..d52099b --- /dev/null +++ b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.java @@ -0,0 +1,53 @@ +/** + * 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.test.AvailablePortFinder; +import org.apache.camel.test.spring.CamelSpringTestSupport; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class SpringRestRestletPojoInOutTest extends CamelSpringTestSupport { + + protected static int portNum; + + @BeforeClass + public static void initializePortNum() { + portNum = AvailablePortFinder.getNextAvailable(); + System.getProperties().setProperty("test.port", "" + portNum); + } + + @Override + protected AbstractApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.xml"); + } + + @Test + public void testRestletPojoInOut() throws Exception { + String body = "{\"id\": 123, \"name\": \"Donald Duck\"}"; + String out = template.requestBody("http://localhost:" + portNum + "/users/lives", body, String.class); + + assertNotNull(out); + assertEquals("{\"iso\":\"EN\",\"country\":\"England\"}", out); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/2d38cd30/components/camel-restlet/src/test/resources/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.xml ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/test/resources/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.xml b/components/camel-restlet/src/test/resources/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.xml new file mode 100644 index 0000000..5b2d6be --- /dev/null +++ b/components/camel-restlet/src/test/resources/org/apache/camel/component/restlet/SpringRestRestletPojoInOutTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!-- + 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> + + <bean id="propertyPlaceholderConfigurer" + class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> + <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> + <property name="searchSystemEnvironment" value="true" /> + </bean> + + <bean id="restlet" class="org.apache.camel.component.restlet.RestletComponent"> + <property name="port" value="${test.port}"/> + </bean> + + <camelContext xmlns="http://camel.apache.org/schema/spring"> + + <restConfiguration bindingMode="auto" component="restlet"/> + + <rest uri="/users"> + <post uri="/lives" type="org.apache.camel.component.restlet.UserPojo" outType="org.apache.camel.component.restlet.CountryPojo"> + <route> + <bean beanType="org.apache.camel.component.restlet.UserService" method="livesWhere"/> + </route> + </post> + </rest> + + </camelContext> + +</beans> \ No newline at end of file