This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit 3f4ed8de511d98d7288b0a1caff05d77d3fa8ae5 Author: Andrea Cosentino <anco...@gmail.com> AuthorDate: Wed May 29 09:13:06 2019 +0200 CAMEL-10324 - Camel-CBOR first commit --- components/camel-cbor/pom.xml | 85 +++++++++++ .../camel-cbor/src/main/docs/jolt-component.adoc | 165 ++++++++++++++++++++ .../apache/camel/component/cbor/CBORConstants.java | 25 +++ .../camel/component/cbor/CBORDataFormat.java | 170 +++++++++++++++++++++ .../org/apache/camel/component/cbor/Author.java | 22 +++ .../camel/component/cbor/CBORDataFormatTest.java | 88 +++++++++++ .../src/test/resources/log4j2.properties | 28 ++++ 7 files changed, 583 insertions(+) diff --git a/components/camel-cbor/pom.xml b/components/camel-cbor/pom.xml new file mode 100644 index 0000000..78ef74d --- /dev/null +++ b/components/camel-cbor/pom.xml @@ -0,0 +1,85 @@ +<?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. + +--> +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>components</artifactId> + <groupId>org.apache.camel</groupId> + <version>3.0.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-cbor</artifactId> + <packaging>jar</packaging> + <name>Camel :: CBOR</name> + <description>Camel Cbor Support</description> + + <properties> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-support</artifactId> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-cbor</artifactId> + <version>${jackson2-version}</version> + </dependency> + + <!-- logging --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + <scope>test</scope> + </dependency> + + <!-- testing --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> diff --git a/components/camel-cbor/src/main/docs/jolt-component.adoc b/components/camel-cbor/src/main/docs/jolt-component.adoc new file mode 100644 index 0000000..bb517e8 --- /dev/null +++ b/components/camel-cbor/src/main/docs/jolt-component.adoc @@ -0,0 +1,165 @@ +[[jolt-component]] +== JOLT Component + +*Available as of Camel version 2.16* + +The *jolt:* component allows you to process a JSON messages using an +https://github.com/bazaarvoice/jolt[JOLT] specification. This can be +ideal when doing JSON to JSON transformation. + +Maven users will need to add the following dependency to +their `pom.xml` for this component: + +[source,xml] +------------------------------------------------------------ +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-jolt</artifactId> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +------------------------------------------------------------ + + + +### URI format + +[source,java] +----------------------- +jolt:specName[?options] +----------------------- + +Where *specName* is the classpath-local URI of the specification to +invoke; or the complete URL of the remote specification +(eg: file://folder/myfile.vm[file://folder/myfile.json]). + +You can append query options to the URI in the following +format, `?option=value&option=value&...` + +### Options + + + + +// component options: START +The JOLT component supports 3 options, which are listed below. + + + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *transform* (advanced) | Explicitly sets the Transform to use. If not set a Transform specified by the transformDsl will be created | | Transform +| *resolveProperty Placeholders* (advanced) | Whether the component should resolve property placeholders on itself when starting. Only properties which are of String type can use property placeholders. | true | boolean +| *basicPropertyBinding* (advanced) | Whether the component should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities | false | boolean +|=== +// component options: END + + + + + + +// endpoint options: START +The JOLT endpoint is configured using URI syntax: + +---- +jolt:resourceUri +---- + +with the following path and query parameters: + +==== Path Parameters (1 parameters): + + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *resourceUri* | *Required* Path to the resource. You can prefix with: classpath, file, http, ref, or bean. classpath, file and http loads the resource using these protocols (classpath is default). ref will lookup the resource in the registry. bean will call a method on a bean to be used as the resource. For bean you can specify the method name after dot, eg bean:myBean.myMethod. | | String +|=== + + +==== Query Parameters (6 parameters): + + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *contentCache* (producer) | Sets whether to use resource content cache or not | false | boolean +| *inputType* (producer) | Specifies if the input is hydrated JSON or a JSON String. | Hydrated | JoltInputOutputType +| *outputType* (producer) | Specifies if the output should be hydrated JSON or a JSON String. | Hydrated | JoltInputOutputType +| *transformDsl* (producer) | Specifies the Transform DSL of the endpoint resource. If none is specified Chainr will be used. | Chainr | JoltTransformType +| *basicPropertyBinding* (advanced) | Whether the endpoint should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities | false | boolean +| *synchronous* (advanced) | Sets whether synchronous processing should be strictly used, or Camel is allowed to use asynchronous processing (if supported). | false | boolean +|=== +// endpoint options: END +// spring-boot-auto-configure options: START +=== Spring Boot Auto-Configuration + +When using Spring Boot make sure to use the following Maven dependency to have support for auto configuration: + +[source,xml] +---- +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-jolt-starter</artifactId> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +---- + + +The component supports 4 options, which are listed below. + + + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *camel.component.jolt.basic-property-binding* | Whether the component should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities | false | Boolean +| *camel.component.jolt.enabled* | Enable jolt component | true | Boolean +| *camel.component.jolt.resolve-property-placeholders* | Whether the component should resolve property placeholders on itself when starting. Only properties which are of String type can use property placeholders. | true | Boolean +| *camel.component.jolt.transform* | Explicitly sets the Transform to use. If not set a Transform specified by the transformDsl will be created. The option is a com.bazaarvoice.jolt.Transform type. | | String +|=== +// spring-boot-auto-configure options: END + + + + +### Samples + +For example you could use something like + +[source,java] +-------------------------------------- +from("activemq:My.Queue"). + to("jolt:com/acme/MyResponse.json"); +-------------------------------------- + +And a file based resource: + +[source,java] +--------------------------------------------------------------- +from("activemq:My.Queue"). + to("jolt:file://myfolder/MyResponse.json?contentCache=true"). + to("activemq:Another.Queue"); +--------------------------------------------------------------- + +You can also specify what specification the component should use +dynamically via a header, so for example: + +[source,java] +--------------------------------------------------------------------- +from("direct:in"). + setHeader("CamelJoltResourceUri").constant("path/to/my/spec.json"). + to("jolt:dummy"); +--------------------------------------------------------------------- + + + +### See Also + +* Configuring Camel +* Component +* Endpoint +* Getting Started diff --git a/components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORConstants.java b/components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORConstants.java new file mode 100644 index 0000000..e3b6670 --- /dev/null +++ b/components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORConstants.java @@ -0,0 +1,25 @@ +/* + * 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.cbor; + +public final class CBORConstants { + + public static final String UNMARSHAL_TYPE = "CamelCBORUnmarshalType"; + + private CBORConstants() { + } +} diff --git a/components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORDataFormat.java b/components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORDataFormat.java new file mode 100644 index 0000000..085eb8f --- /dev/null +++ b/components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORDataFormat.java @@ -0,0 +1,170 @@ +package org.apache.camel.component.cbor; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Set; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatName; +import org.apache.camel.spi.annotations.Dataformat; +import org.apache.camel.support.service.ServiceSupport; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.dataformat.cbor.CBORFactory; + +@Dataformat("cbor") +public class CBORDataFormat extends ServiceSupport implements DataFormat, DataFormatName { + + private CamelContext camelContext; + private ObjectMapper objectMapper; + private Class<?> unmarshalType; + private boolean useDefaultObjectMapper = true; + private boolean allowUnmarshallType; + private Class<? extends Collection> collectionType; + private boolean useList; + + /** + * Use the default CBOR Jackson {@link ObjectMapper} and {@link Object} + */ + public CBORDataFormat() { + } + + /** + * Use the default CBOR Jackson {@link ObjectMapper} and with a custom unmarshal + * type + * + * @param unmarshalType the custom unmarshal type + */ + public CBORDataFormat(ObjectMapper objectMapper, Class<?> unmarshalType) { + this.unmarshalType = unmarshalType; + this.objectMapper = objectMapper; + } + + @Override + public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception { + stream.write(this.objectMapper.writeValueAsBytes(graph)); + } + + @Override + public Object unmarshal(Exchange exchange, InputStream stream) throws Exception { + Class<?> clazz = unmarshalType; + String type = null; + if (allowUnmarshallType) { + type = exchange.getIn().getHeader(CBORConstants.UNMARSHAL_TYPE, String.class); + } + if (type != null) { + clazz = exchange.getContext().getClassResolver().resolveMandatoryClass(type); + } + if (collectionType != null) { + CollectionType collType = objectMapper.getTypeFactory().constructCollectionType(collectionType, clazz); + return this.objectMapper.readValue(stream, collType); + } else { + return this.objectMapper.readValue(stream, clazz); + } + } + + @Override + public String getDataFormatName() { + return "cbor"; + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public Class<?> getUnmarshalType() { + return unmarshalType; + } + + public void setUnmarshalType(Class<?> unmarshalType) { + this.unmarshalType = unmarshalType; + } + + public boolean isAllowUnmarshallType() { + return allowUnmarshallType; + } + + public void setAllowUnmarshallType(boolean allowUnmarshallType) { + this.allowUnmarshallType = allowUnmarshallType; + } + + public Class<? extends Collection> getCollectionType() { + return collectionType; + } + + public void setCollectionType(Class<? extends Collection> collectionType) { + this.collectionType = collectionType; + } + + public boolean isUseList() { + return useList; + } + + public void setUseList(boolean useList) { + this.useList = useList; + } + + public boolean isUseDefaultObjectMapper() { + return useDefaultObjectMapper; + } + + public void setUseDefaultObjectMapper(boolean useDefaultObjectMapper) { + this.useDefaultObjectMapper = useDefaultObjectMapper; + } + + /** + * Uses {@link java.util.ArrayList} when unmarshalling. + */ + public void useList() { + setCollectionType(ArrayList.class); + } + + /** + * Uses {@link java.util.HashMap} when unmarshalling. + */ + public void useMap() { + setCollectionType(null); + setUnmarshalType(HashMap.class); + } + + @Override + protected void doStart() throws Exception { + if (objectMapper == null) { + // lookup if there is a single default mapper we can use + if (useDefaultObjectMapper && camelContext != null) { + Set<ObjectMapper> set = camelContext.getRegistry().findByType(ObjectMapper.class); + if (set.size() == 1) { + objectMapper = set.iterator().next(); + log.info("Found single ObjectMapper in Registry to use: {}", objectMapper); + } else if (set.size() > 1) { + log.debug("Found {} ObjectMapper in Registry cannot use as default as there are more than one instance.", set.size()); + } + } + if (objectMapper == null) { + CBORFactory factory = new CBORFactory(); + objectMapper = new ObjectMapper(factory); + log.debug("Creating new ObjectMapper to use: {}", objectMapper); + } + } + + if (useList) { + setCollectionType(ArrayList.class); + } + } + + @Override + protected void doStop() throws Exception { + // TODO Auto-generated method stub + + } +} diff --git a/components/camel-cbor/src/test/java/org/apache/camel/component/cbor/Author.java b/components/camel-cbor/src/test/java/org/apache/camel/component/cbor/Author.java new file mode 100644 index 0000000..a6c2cc4 --- /dev/null +++ b/components/camel-cbor/src/test/java/org/apache/camel/component/cbor/Author.java @@ -0,0 +1,22 @@ +package org.apache.camel.component.cbor; + +public class Author { + + private String name; + private String surname; + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getSurname() { + return surname; + } + public void setSurname(String surname) { + this.surname = surname; + } + + +} diff --git a/components/camel-cbor/src/test/java/org/apache/camel/component/cbor/CBORDataFormatTest.java b/components/camel-cbor/src/test/java/org/apache/camel/component/cbor/CBORDataFormatTest.java new file mode 100644 index 0000000..4ca359e --- /dev/null +++ b/components/camel-cbor/src/test/java/org/apache/camel/component/cbor/CBORDataFormatTest.java @@ -0,0 +1,88 @@ +/* + * 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.cbor; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class CBORDataFormatTest extends CamelTestSupport { + + @Test + public void testMarshalAndUnmarshalMap() throws Exception { + Map<String, Object> in = new HashMap<>(); + in.put("name", "Camel"); + + MockEndpoint mock = getMockEndpoint("mock:reverse"); + mock.expectedMessageCount(1); + mock.message(0).body().isInstanceOf(Map.class); + mock.message(0).body().isEqualTo(in); + + Object marshalled = template.requestBody("direct:in", in); + + template.sendBody("direct:back", marshalled); + + mock.assertIsSatisfied(); + } + + @Test + public void testMarshalAndUnmarshalAuthor() throws Exception { + Author auth = new Author(); + auth.setName("Don"); + auth.setSurname("Winslow"); + + MockEndpoint mock = getMockEndpoint("mock:reverse-auth"); + mock.expectedMessageCount(1); + mock.message(0).body().isInstanceOf(Author.class); + + Object marshalled = template.requestBody("direct:in-auth", auth); + + template.sendBody("direct:back-auth", marshalled); + + Author authReturned = mock.getExchanges().get(0).getIn().getBody(Author.class); + assertEquals("Don", authReturned.getName()); + assertEquals("Winslow", authReturned.getSurname()); + + mock.assertIsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + + @Override + public void configure() throws Exception { + CBORDataFormat format = new CBORDataFormat(); + format.useMap(); + + from("direct:in").marshal(format); + from("direct:back").unmarshal(format).to("mock:reverse"); + + CBORDataFormat spec = new CBORDataFormat(); + spec.setUnmarshalType(Author.class); + + from("direct:in-auth").marshal(spec); + from("direct:back-auth").unmarshal(spec).to("mock:reverse-auth"); + } + }; + } + +} diff --git a/components/camel-cbor/src/test/resources/log4j2.properties b/components/camel-cbor/src/test/resources/log4j2.properties new file mode 100644 index 0000000..85ea6dc --- /dev/null +++ b/components/camel-cbor/src/test/resources/log4j2.properties @@ -0,0 +1,28 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +appender.file.type = File +appender.file.name = file +appender.file.fileName = target/camel-jolt-test.log +appender.file.layout.type = PatternLayout +appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +appender.stdout.type = Console +appender.stdout.name = stdout +appender.stdout.layout.type = PatternLayout +appender.stdout.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level = INFO +rootLogger.appenderRef.file.ref = file