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 2b865af77ecd33db306b6b1a64f6b4f6314e2c4b Author: Jan Bednar <[email protected]> AuthorDate: Fri May 31 14:29:42 2019 +0200 CAMEL-10540: Implemented Grok DataFormat --- apache-camel/pom.xml | 10 + apache-camel/src/main/descriptors/common-bin.xml | 2 + bom/camel-bom/pom.xml | 10 + components/camel-grok/pom.xml | 74 ++++++++ .../camel-grok/src/main/docs/grok-dataformat.adoc | 138 ++++++++++++++ .../camel/component/grok/GrokDataFormat.java | 185 +++++++++++++++++++ .../apache/camel/component/grok/GrokPattern.java | 64 +++++++ .../component/grok/GrokFileUnmarshalTest.java | 48 +++++ .../camel/component/grok/GrokMarshalTest.java | 50 +++++ .../component/grok/GrokOptionalOptionsTest.java | 123 +++++++++++++ .../camel/component/grok/GrokPatternsTest.java | 102 +++++++++++ .../camel/component/grok/GrokUnmarshalTest.java | 111 +++++++++++ .../component/grok/SpringGrokDataFormatTest.java | 86 +++++++++ .../src/test/resources/log4j2.properties | 27 +++ .../component/grok/SpringGrokDataFormatTest.xml | 70 +++++++ .../apache/camel/component/grok/data/access_log | 5 + components/pom.xml | 1 + components/readme.adoc | 4 +- .../org/apache/camel/builder/DataFormatClause.java | 10 + .../apache/camel/model/UnmarshalDefinition.java | 2 + .../model/dataformat/DataFormatsDefinition.java | 1 + .../camel/model/dataformat/GrokDataFormat.java | 104 +++++++++++ .../DataFormatTransformerDefinition.java | 2 + .../reifier/dataformat/DataFormatReifier.java | 2 + .../reifier/dataformat/GrokDataFormatReifier.java | 40 ++++ docs/components/modules/ROOT/nav.adoc | 1 + .../modules/ROOT/pages/grok-dataformat.adoc | 138 ++++++++++++++ parent/pom.xml | 18 ++ .../components-starter/camel-grok-starter/pom.xml | 53 ++++++ .../GrokDataFormatAutoConfiguration.java | 128 +++++++++++++ .../springboot/GrokDataFormatConfiguration.java | 107 +++++++++++ .../src/main/resources/META-INF/LICENSE.txt | 203 +++++++++++++++++++++ .../src/main/resources/META-INF/NOTICE.txt | 11 ++ .../src/main/resources/META-INF/spring.factories | 19 ++ .../src/main/resources/META-INF/spring.provides | 17 ++ platforms/spring-boot/components-starter/pom.xml | 1 + .../camel-spring-boot-dependencies/pom.xml | 15 ++ 37 files changed, 1981 insertions(+), 1 deletion(-) diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml index 44237a3..badecbc 100644 --- a/apache-camel/pom.xml +++ b/apache-camel/pom.xml @@ -628,6 +628,11 @@ </dependency> <dependency> <groupId>org.apache.camel</groupId> + <artifactId>camel-grok</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> <artifactId>camel-groovy</artifactId> <version>${project.version}</version> </dependency> @@ -2117,6 +2122,11 @@ </dependency> <dependency> <groupId>org.apache.camel</groupId> + <artifactId>camel-grok-starter</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> <artifactId>camel-groovy-starter</artifactId> <version>${project.version}</version> </dependency> diff --git a/apache-camel/src/main/descriptors/common-bin.xml b/apache-camel/src/main/descriptors/common-bin.xml index d2edfd0..cda385a 100644 --- a/apache-camel/src/main/descriptors/common-bin.xml +++ b/apache-camel/src/main/descriptors/common-bin.xml @@ -147,6 +147,7 @@ <include>org.apache.camel:camel-google-sheets</include> <include>org.apache.camel:camel-gora</include> <include>org.apache.camel:camel-grape</include> + <include>org.apache.camel:camel-grok</include> <include>org.apache.camel:camel-groovy</include> <include>org.apache.camel:camel-grpc</include> <include>org.apache.camel:camel-gson</include> @@ -483,6 +484,7 @@ <include>org.apache.camel:camel-google-sheets-starter</include> <include>org.apache.camel:camel-gora-starter</include> <include>org.apache.camel:camel-grape-starter</include> + <include>org.apache.camel:camel-grok-starter</include> <include>org.apache.camel:camel-groovy-starter</include> <include>org.apache.camel:camel-grpc-starter</include> <include>org.apache.camel:camel-gson-starter</include> diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml index 4a1ed0b..39961eb 100644 --- a/bom/camel-bom/pom.xml +++ b/bom/camel-bom/pom.xml @@ -1139,6 +1139,16 @@ </dependency> <dependency> <groupId>org.apache.camel</groupId> + <artifactId>camel-grok</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-grok-starter</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> <artifactId>camel-groovy</artifactId> <version>${project.version}</version> </dependency> diff --git a/components/camel-grok/pom.xml b/components/camel-grok/pom.xml new file mode 100644 index 0000000..d1d300d --- /dev/null +++ b/components/camel-grok/pom.xml @@ -0,0 +1,74 @@ +<?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/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>3.0.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-grok</artifactId> + <packaging>jar</packaging> + + <name>Camel :: Grok</name> + <description>Camel Grok support</description> + + <dependencies> + + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-support</artifactId> + </dependency> + <dependency> + <groupId>io.krakens</groupId> + <artifactId>java-grok</artifactId> + </dependency> + + <!-- testing --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-spring</artifactId> + <scope>test</scope> + </dependency> + <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> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/components/camel-grok/src/main/docs/grok-dataformat.adoc b/components/camel-grok/src/main/docs/grok-dataformat.adoc new file mode 100644 index 0000000..e160960 --- /dev/null +++ b/components/camel-grok/src/main/docs/grok-dataformat.adoc @@ -0,0 +1,138 @@ +[[grok-dataformat]] +== Grok DataFormat + +*Available as of Camel version 3.0* + +This component provides dataformat for processing inputs with grok patterns. +Grok patterns are used to process unstructured data into structured objects - `List<Map<String, Object>`. + +This component is based on https://github.com/thekrakken/java-grok[Java Grok library] + +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-grok</artifactId> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +------------------------------------------------------------ + +### Basic usage + +Extract all IP adresses from input +[source,java] +-------------------------------------------------------------------------------- +from("direct:in") + .unmarshal().grok("%{IP:ip}") + .to("log:out"); +-------------------------------------------------------------------------------- + +Parse Apache logs and process only 4xx responses +[source,java] +-------------------------------------------------------------------------------- +from("file://apacheLogs") + .unmarshal().grok("%{COMBINEDAPACHELOG") + .split(body()).filter(simple("${body[response]} starts with '4'")) + .to("log:4xx") +-------------------------------------------------------------------------------- + +### Predefined patterns + +This component comes with predefined patterns, which are based on Logstash patterns. +Full list can be found at https://github.com/thekrakken/java-grok/tree/master/src/main/resources/patterns[Java Grok repository] + +### Custom patterns + +Camel Grok DataFormat supports plugable patterns, which are auto loaded from Camel Registry. +You can register patterns with Java DSL and Spring DSL + +Spring DSL: +[source,xml] +-------------------------------------------------------------------------------- +<beans> + <bean id="myCustomPatternBean" class="org.apache.camel.component.grok.GrokPattern"> + <constructor-arg value="FOOBAR"/> + <constructor-arg value="foo|bar"/> + </bean> +<beans> +<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> + <route> + <from uri="direct:in"/> + <unmarshal> + <grok pattern="%{FOOBAR:fooBar}"/> + </unmarshal> + <to uri="log:out"/> + </route> +</camelContext> +-------------------------------------------------------------------------------- + +Java DSL: +[source,java] +-------------------------------------------------------------------------------- +public class MyRouteBuilder extends RouteBuilder { + + @Override + public void configure() throws Exception { + bindToRegistry("myCustomPatternBean", new GrokPattern("FOOBAR", "foo|bar")); + + from("direct:in") + .unmarshal().grok("%{FOOBAR:fooBar}") + .to("log:out"); + } +} +-------------------------------------------------------------------------------- + + +### Grok Dataformat Options + +// dataformat options: START +The Grok dataformat supports 5 options, which are listed below. + + + +[width="100%",cols="2s,1m,1m,6",options="header"] +|=== +| Name | Default | Java Type | Description +| pattern | | String | The grok pattern to match lines of input +| flattened | false | Boolean | Turns on flattened mode. In flattened mode the exception is thrown when there are multiple pattern matches with same key. +| allowMultipleMatchesPerLine | true | Boolean | If false, every line of input is matched for pattern only once. Otherwise the line can be scanned multiple times when non-terminal pattern is used. +| namedOnly | false | Boolean | Whether to capture named expressions only or not (i.e. %{IP:ip} but not ${IP}) +| contentTypeHeader | false | Boolean | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc. +|=== +// dataformat 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-grok-starter</artifactId> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +---- + + +The component supports 6 options, which are listed below. + + + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *camel.dataformat.grok.allow-multiple-matches-per-line* | If false, every line of input is matched for pattern only once. Otherwise the line can be scanned multiple times when non-terminal pattern is used. | true | Boolean +| *camel.dataformat.grok.content-type-header* | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc. | false | Boolean +| *camel.dataformat.grok.enabled* | Whether to enable auto configuration of the grok data format. This is enabled by default. | | Boolean +| *camel.dataformat.grok.flattened* | Turns on flattened mode. In flattened mode the exception is thrown when there are multiple pattern matches with same key. | false | Boolean +| *camel.dataformat.grok.named-only* | Whether to capture named expressions only or not (i.e. %{IP:ip} but not ${IP}) | false | Boolean +| *camel.dataformat.grok.pattern* | The grok pattern to match lines of input | | String +|=== +// spring-boot-auto-configure options: END +ND diff --git a/components/camel-grok/src/main/java/org/apache/camel/component/grok/GrokDataFormat.java b/components/camel-grok/src/main/java/org/apache/camel/component/grok/GrokDataFormat.java new file mode 100644 index 0000000..0ccc53a --- /dev/null +++ b/components/camel-grok/src/main/java/org/apache/camel/component/grok/GrokDataFormat.java @@ -0,0 +1,185 @@ +/* + * 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.grok; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.CharBuffer; +import java.util.*; +import java.util.stream.Stream; + +import io.krakens.grok.api.Grok; +import io.krakens.grok.api.GrokCompiler; +import io.krakens.grok.api.Match; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +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.ExchangeHelper; +import org.apache.camel.support.service.ServiceSupport; +import org.apache.camel.util.ObjectHelper; + +@Dataformat("grok") +public class GrokDataFormat extends ServiceSupport implements DataFormat, DataFormatName, CamelContextAware { + private CamelContext camelContext; + + private Grok grok; + + private boolean allowMultipleMatchesPerLine = true; + private Set<GrokPattern> customPatterns = new HashSet<>(); + private boolean flattened; + private boolean namedOnly; + private String pattern; + + public GrokDataFormat(String pattern) { + this.pattern = pattern; + } + + public GrokDataFormat() { + } + + public GrokDataFormat registerPatternDefinition(GrokPattern pattern) { + this.customPatterns.add(pattern); + this.refreshGrok(); + return this; + } + + /** + * @param name : Pattern Name + * @param pattern : Regular expression Or Grok pattern + */ + public GrokDataFormat registerPatternDefinition(String name, String pattern) { + return registerPatternDefinition(new GrokPattern(name, pattern)); + } + + public GrokDataFormat setPattern(String pattern) { + this.pattern = pattern; + return this; + } + + /** + * Sets the flattened mode flag + * @param flattened If true, conversion throws exception for conficting named matches. + */ + public GrokDataFormat setFlattened(boolean flattened) { + this.flattened = flattened; + return this; + } + + public GrokDataFormat setAllowMultipleMatchesPerLine(boolean allowMultipleMatchesPerLine) { + this.allowMultipleMatchesPerLine = allowMultipleMatchesPerLine; + return this; + } + + /** + * Whether to capture named expressions only or not (i.e. %{IP:ip} but not ${IP}) + */ + public GrokDataFormat setNamedOnly(boolean namedOnly) { + this.namedOnly = namedOnly; + return this; + } + + private void refreshGrok() { + ObjectHelper.notNull(pattern, "pattern"); + + GrokCompiler grokCompiler = GrokCompiler.newInstance(); + grokCompiler.registerDefaultPatterns(); + for (GrokPattern pattern : customPatterns) { + grokCompiler.register(pattern.getName(), pattern.getPattern()); + } + grok = grokCompiler.compile(pattern, namedOnly); + } + + @Override + public String getDataFormatName() { + return "grok"; + } + + @Override + public CamelContext getCamelContext() { + return camelContext; + } + + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + + @Override + public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception { + throw new UnsupportedOperationException("GrokDataFormat does not support marshalling. Use unmarshal instead."); + } + + @Override + public Object unmarshal(Exchange exchange, InputStream stream) throws Exception { + List<Map<String, Object>> result = new ArrayList<>(); + + InputStreamReader in = new InputStreamReader(stream, ExchangeHelper.getCharsetName(exchange)); + try (Stream<String> lines = new BufferedReader(in).lines()) { + lines.forEachOrdered(line -> processLine(line, result)); + } + + if (result.size() == 0) { + return null; + } + + if (result.size() == 1) { + return result.get(0); + } + + return result; + } + + private void processLine(String line, List<Map<String, Object>> resultList) { + CharBuffer charBuffer = CharBuffer.wrap(line); + + int start = 0; + while (start < charBuffer.length()) { //Allow multiple matches per line + Match gm = grok.match(charBuffer.subSequence(start, charBuffer.length())); + if (!gm.isNull()) { + if (flattened) { + resultList.add(gm.captureFlattened()); + } else { + resultList.add(gm.capture()); + } + start += gm.getEnd(); + } else { + break; + } + + if (!allowMultipleMatchesPerLine) { + break; + } + } + } + + @Override + protected void doStart() throws Exception { + customPatterns.addAll(getCamelContext().getRegistry().findByType(GrokPattern.class)); + refreshGrok(); + } + + @Override + protected void doStop() throws Exception { + //noop + } + +} diff --git a/components/camel-grok/src/main/java/org/apache/camel/component/grok/GrokPattern.java b/components/camel-grok/src/main/java/org/apache/camel/component/grok/GrokPattern.java new file mode 100644 index 0000000..8eaaf85 --- /dev/null +++ b/components/camel-grok/src/main/java/org/apache/camel/component/grok/GrokPattern.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.component.grok; + +public class GrokPattern { + private String name; + private String pattern; + + public GrokPattern(String name, String pattern) { + this.name = name; + this.pattern = pattern; + } + + protected String getName() { + return this.name; + } + + protected String getPattern() { + return this.pattern; + } + + @Override + public String toString() { + return "GrokPattern[" + getName() + " = '" + getPattern() + "']"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GrokPattern that = (GrokPattern) o; + + if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) { + return false; + } + return getPattern() != null ? getPattern().equals(that.getPattern()) : that.getPattern() == null; + } + + @Override + public int hashCode() { + int result = getName() != null ? getName().hashCode() : 0; + result = 31 * result + (getPattern() != null ? getPattern().hashCode() : 0); + return result; + } +} diff --git a/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokFileUnmarshalTest.java b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokFileUnmarshalTest.java new file mode 100644 index 0000000..c93141e --- /dev/null +++ b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokFileUnmarshalTest.java @@ -0,0 +1,48 @@ +/* + * 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.grok; + +import java.util.List; + +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; + +public class GrokFileUnmarshalTest extends CamelTestSupport { + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("file:src/test/resources/org/apache/camel/component/grok/data?fileName=access_log&noop=true") + .unmarshal().grok("%{COMMONAPACHELOG}") + .to("mock:apachelog"); + } + }; + } + + @Test + public void test() throws Exception { + MockEndpoint result = getMockEndpoint("mock:apachelog"); + result.expectedMessageCount(1); + result.assertIsSatisfied(); + Assert.assertEquals(5, result.getExchanges().get(0).getIn().getBody(List.class).size()); + } +} diff --git a/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokMarshalTest.java b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokMarshalTest.java new file mode 100644 index 0000000..eb5b4d9 --- /dev/null +++ b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokMarshalTest.java @@ -0,0 +1,50 @@ +/* + * 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.grok; + +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; + +public class GrokMarshalTest extends CamelTestSupport { + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:direct") + .marshal().grok("%{IP:ip}") + .to("mock:mock"); + + } + }; + } + + @Test + public void testMarshalNotSupported() throws Exception { + try { + template.sendBody("direct:direct", ""); + Assert.fail("Should throw exception"); + } catch (Exception e) { + Assert.assertNotNull(e.getCause()); + Assert.assertEquals(UnsupportedOperationException.class, e.getCause().getClass()); + } + } + +} diff --git a/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokOptionalOptionsTest.java b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokOptionalOptionsTest.java new file mode 100644 index 0000000..8a172b6 --- /dev/null +++ b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokOptionalOptionsTest.java @@ -0,0 +1,123 @@ +/* + * 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.grok; + +import java.util.List; +import java.util.Map; + +import io.krakens.grok.api.exception.GrokException; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spi.DataFormat; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; + +public class GrokOptionalOptionsTest extends CamelTestSupport { + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + DataFormat grokFlattenedTrue = new GrokDataFormat("%{INT:i} %{INT:i}") + .setFlattened(true); + DataFormat grokFlattenedFalse = new GrokDataFormat("%{INT:i} %{INT:i}") + .setFlattened(false); + + DataFormat grokNamedOnlyTrue = new GrokDataFormat("%{URI:website}") + .setNamedOnly(true); + DataFormat grokNamedOnlyFalse = new GrokDataFormat("%{URI:website}") + .setNamedOnly(false); + + DataFormat grokAllowMultipleMatchesPerLineTrue = new GrokDataFormat("%{INT:i}") + .setAllowMultipleMatchesPerLine(true); + DataFormat grokAllowMultipleMatchesPerLineFalse = new GrokDataFormat("%{INT:i}") + .setAllowMultipleMatchesPerLine(false); + + from("direct:flattenedTrue").unmarshal(grokFlattenedTrue); + from("direct:flattenedFalse").unmarshal(grokFlattenedFalse); + from("direct:namedOnlyTrue").unmarshal(grokNamedOnlyTrue); + from("direct:namedOnlyFalse").unmarshal(grokNamedOnlyFalse); + from("direct:allowMultipleMatchesPerLineTrue").unmarshal(grokAllowMultipleMatchesPerLineTrue); + from("direct:allowMultipleMatchesPerLineFalse").unmarshal(grokAllowMultipleMatchesPerLineFalse); + + } + }; + } + + @Test + @SuppressWarnings("unchecked") + public void testFlattened() throws Exception { + Map<String, Object> flattenedFalse = template.requestBody("direct:flattenedFalse", "123 456", Map.class); + Assert.assertNotNull(flattenedFalse); + Assert.assertTrue(flattenedFalse.containsKey("i")); + Assert.assertTrue(flattenedFalse.get("i") instanceof List); + Assert.assertEquals("123", ((List)flattenedFalse.get("i")).get(0)); + Assert.assertEquals("456", ((List)flattenedFalse.get("i")).get(1)); + + try { + template.requestBody("direct:flattenedTrue", "1 2"); + Assert.fail("Should throw exception"); + } catch (Exception e) { + Assert.assertNotNull(e.getCause()); + Assert.assertTrue("Should throw GrokException", e.getCause() instanceof GrokException); + } + } + + + @Test + @SuppressWarnings("unchecked") + public void testNamedOnly() throws Exception { + Map<String, Object> namedOnlyTrue = template.requestBody("direct:namedOnlyTrue", "https://github.com/apache/camel", Map.class); + Assert.assertNotNull(namedOnlyTrue); + Assert.assertEquals("https://github.com/apache/camel", namedOnlyTrue.get("website")); + Assert.assertFalse(namedOnlyTrue.containsKey("URIPROTO")); + Assert.assertFalse(namedOnlyTrue.containsKey("URIHOST")); + Assert.assertFalse(namedOnlyTrue.containsKey("URIPATHPARAM")); + + Map<String, Object> namedOnlyFalse = template.requestBody("direct:namedOnlyFalse", "https://github.com/apache/camel", Map.class); + Assert.assertNotNull(namedOnlyFalse); + Assert.assertEquals("https://github.com/apache/camel", namedOnlyFalse.get("website")); + Assert.assertEquals("https", namedOnlyFalse.get("URIPROTO")); + Assert.assertEquals("github.com", namedOnlyFalse.get("URIHOST")); + Assert.assertEquals("/apache/camel", namedOnlyFalse.get("URIPATHPARAM")); + } + + @Test + @SuppressWarnings("unchecked") + public void testAllowMultipleMatchesPerLine() throws Exception { + List<Map<String, Object>> allowMultipleMatchesPerLineTrue = template.requestBody( + "direct:allowMultipleMatchesPerLineTrue", + "1 2 \n 3", + List.class); + Assert.assertNotNull(allowMultipleMatchesPerLineTrue); + Assert.assertEquals(3, allowMultipleMatchesPerLineTrue.size()); + Assert.assertEquals("1", allowMultipleMatchesPerLineTrue.get(0).get("i")); + Assert.assertEquals("2", allowMultipleMatchesPerLineTrue.get(1).get("i")); + Assert.assertEquals("3", allowMultipleMatchesPerLineTrue.get(2).get("i")); + + List<Map<String, Object>> allowMultipleMatchesPerLineFalse = template.requestBody( + "direct:allowMultipleMatchesPerLineFalse", + "1 2 \n 3", + List.class); + Assert.assertNotNull(allowMultipleMatchesPerLineFalse); + Assert.assertEquals(2, allowMultipleMatchesPerLineFalse.size()); + Assert.assertEquals("1", allowMultipleMatchesPerLineFalse.get(0).get("i")); + Assert.assertEquals("3", allowMultipleMatchesPerLineFalse.get(1).get("i")); + + } +} diff --git a/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokPatternsTest.java b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokPatternsTest.java new file mode 100644 index 0000000..7a4e22a --- /dev/null +++ b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokPatternsTest.java @@ -0,0 +1,102 @@ +/* + * 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.grok; + +import java.time.Instant; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Consumer; + +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(value = Parameterized.class) +public class GrokPatternsTest extends CamelTestSupport { + + private String pattern; + private String input; + private Consumer<Map> expectedOutputTest; + + public GrokPatternsTest(String pattern, String input, Consumer<Map> expectedOutputTest) { + this.pattern = pattern; + this.input = input; + this.expectedOutputTest = expectedOutputTest; + } + + @Parameterized.Parameters(name = "{index}: (\"{0}\",\"{1}\") matches {2}") + public static Object[][] data() { + String randomUuid = UUID.randomUUID().toString(); + return new Object[][]{ + {"%{QS:qs}", "this is some \"quoted string\".", test("qs", "quoted string")}, + {"%{UUID:uuid}", "some " + randomUuid, test("uuid", randomUuid)}, + {"%{MAC:mac}", "some:invalid:prefix:of:eth0:02:00:4c:4f:4f:50", test("mac", "02:00:4c:4f:4f:50")}, + {"%{PATH:path}", "C:\\path\\file", test("path", "C:\\path\\file")}, + {"%{PATH:path}", "C:\\path\\file.txt", test("path", "C:\\path\\file.txt")}, + {"%{PATH:path}", "\\\\server\\share\\path\\file", test("path", "\\\\server\\share\\path\\file")}, + {"%{PATH:path}", "/root/.hidden_file", test("path", "/root/.hidden_file")}, + {"%{PATH:path}", "/home/user/../../mnt", test("path", "/home/user/../../mnt")}, + {"%{PATH:path}", "/root", test("path", "/root")}, + {"%{URI:camelSite}", "the site is at http://camel.apache.org/", test("camelSite", "http://camel.apache.org/")}, + {"%{URI:camelSite}", "the dataformat docs is at http://camel.apache.org/data-format.html", test("camelSite", "http://camel.apache.org/data-format.html")}, + {"%{NUMBER:num}", "number is 123.", test("num", "123")}, + {"%{NUMBER:num:integer}", "number is 123.", test("num", 123)}, + {"%{IP:ip}", "my ip is 192.168.0.1", test("ip", "192.168.0.1")}, + {"%{TIMESTAMP_ISO8601:timestamp}", "This test was created at 2019-05-26T10:54:15Z test plain", test("timestamp", "2019-05-26T10:54:15Z")}, + {"%{TIMESTAMP_ISO8601:timestamp:date}", "This test was created at 2019-05-26T10:54:15Z test convert", test("timestamp", Instant.ofEpochSecond(1558868055))}, + }; + } + + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:input") + .unmarshal().grok(pattern); + } + }; + } + + @Test + public void testPattern() throws Exception { + expectedOutputTest.accept( + template.requestBody("direct:input", input, Map.class) + ); + } + + private static Consumer<Map> test(String key, Object value) { + return new Consumer<Map>() { + @Override + public void accept(Map m) { + boolean result = m != null && m.containsKey(key) && Objects.equals(m.get(key), value); + Assert.assertTrue(String.format("Expected: map.get(%s) == %s. Given map %s", key, value, m), result); + } + + @Override + public String toString() { + return String.format("map[%s] = %s", key, value); + } + }; + } + +} diff --git a/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokUnmarshalTest.java b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokUnmarshalTest.java new file mode 100644 index 0000000..904aee2 --- /dev/null +++ b/components/camel-grok/src/test/java/org/apache/camel/component/grok/GrokUnmarshalTest.java @@ -0,0 +1,111 @@ +/* + * 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.grok; + +import java.util.List; +import java.util.Map; + +import org.apache.camel.Exchange; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Assert; +import org.junit.Test; + +public class GrokUnmarshalTest extends CamelTestSupport { + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + bindToRegistry("myCustomPatternBean", new GrokPattern("FOOBAR", "foo|bar")); + bindToRegistry("myAnotherCustomPatternBean", + new GrokPattern("FOOBAR_WITH_PREFIX_AND_SUFFIX", "-- %{FOOBAR}+ --")); + + from("direct:ip") + .unmarshal().grok("%{IP:ip}") + .to("mock:ip"); + + from("direct:fooBar") + .unmarshal().grok("%{FOOBAR_WITH_PREFIX_AND_SUFFIX:fooBar}") + .to("mock:fooBar"); + } + }; + } + + @Test + public void testSingleIp() throws Exception { + MockEndpoint result = getMockEndpoint("mock:ip"); + template.sendBody("direct:ip", "178.21.82.201"); + result.expectedMessageCount(1); + result.assertIsSatisfied(); + Assert.assertEquals("178.21.82.201", result.getExchanges().get(0).getIn().getBody(Map.class).get("ip")); + } + + @Test + public void testMultipleIpSingleLine() throws Exception { + MockEndpoint result = getMockEndpoint("mock:ip"); + template.sendBody("direct:ip", "178.21.82.201 178.21.82.202 178.21.82.203 178.21.82.204"); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + Assert.assertEquals("178.21.82.201", get(result, 0, 0, "ip")); + Assert.assertEquals("178.21.82.202", get(result, 0, 1, "ip")); + Assert.assertEquals("178.21.82.203", get(result, 0, 2, "ip")); + Assert.assertEquals("178.21.82.204", get(result, 0, 3, "ip")); + } + + @Test + public void testMultipleIpMultipleLineMixedLineEndings() throws Exception { + MockEndpoint result = getMockEndpoint("mock:ip"); + template.sendBody("direct:ip", "178.21.82.201 178.21.82.202\n178.21.82.203\r\n178.21.82.204"); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + Assert.assertEquals("178.21.82.201", get(result, 0, 0, "ip")); + Assert.assertEquals("178.21.82.202", get(result, 0, 1, "ip")); + Assert.assertEquals("178.21.82.203", get(result, 0, 2, "ip")); + Assert.assertEquals("178.21.82.204", get(result, 0, 3, "ip")); + } + + @Test + public void testCustomPattern() throws Exception { + MockEndpoint result = getMockEndpoint("mock:fooBar"); + template.sendBody("direct:fooBar", "bar foobar bar -- barbarfoobarfoobar -- barbar"); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + Assert.assertEquals( + "-- barbarfoobarfoobar --", + result.getExchanges().get(0).getIn().getBody(Map.class).get("fooBar") + ); + } + + private Object get(Exchange exchange, int listIndex, String mapKey) { + Assert.assertNotNull("Body should not be null", exchange.getIn().getBody(List.class)); + List list = exchange.getIn().getBody(List.class); + Assert.assertTrue(list.size() > listIndex); + Assert.assertTrue(list.get(listIndex) instanceof Map); + return ((Map) list.get(listIndex)).get(mapKey); + } + + private Object get(MockEndpoint mockEndpoint, int exchangeIndex, int listIndex, String mapKey) { + Assert.assertTrue(mockEndpoint.getExchanges().size() > exchangeIndex); + return get(mockEndpoint.getExchanges().get(exchangeIndex), listIndex, mapKey); + } +} diff --git a/components/camel-grok/src/test/java/org/apache/camel/component/grok/SpringGrokDataFormatTest.java b/components/camel-grok/src/test/java/org/apache/camel/component/grok/SpringGrokDataFormatTest.java new file mode 100644 index 0000000..d713cfb --- /dev/null +++ b/components/camel-grok/src/test/java/org/apache/camel/component/grok/SpringGrokDataFormatTest.java @@ -0,0 +1,86 @@ +/* + * 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.grok; + +import java.util.List; +import java.util.Map; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.spring.CamelSpringTestSupport; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class SpringGrokDataFormatTest extends CamelSpringTestSupport { + + @Test + public void testMultipleIpCustomRef() throws Exception { + MockEndpoint result = getMockEndpoint("mock:ipCustom"); + template.sendBody("direct:ipCustom", "178.21.82.201 178.21.82.202\n178.21.82.203\r\n178.21.82.204"); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> body = result.getExchanges().get(0).getIn().getBody(List.class); + + Assert.assertEquals(4, body.size()); + + Assert.assertEquals("178.21.82.201", body.get(0).get("ip")); + Assert.assertEquals("178.21.82.202", body.get(1).get("ip")); + Assert.assertEquals("178.21.82.203", body.get(2).get("ip")); + Assert.assertEquals("178.21.82.204", body.get(3).get("ip")); + } + + @Test + public void testMultipleIp() throws Exception { + MockEndpoint result = getMockEndpoint("mock:ip"); + template.sendBody("direct:ip", "178.21.82.201 178.21.82.202\n178.21.82.203\r\n178.21.82.204"); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> body = result.getExchanges().get(0).getIn().getBody(List.class); + + Assert.assertEquals(4, body.size()); + + Assert.assertEquals("178.21.82.201", body.get(0).get("ip")); + Assert.assertEquals("178.21.82.202", body.get(1).get("ip")); + Assert.assertEquals("178.21.82.203", body.get(2).get("ip")); + Assert.assertEquals("178.21.82.204", body.get(3).get("ip")); + } + + @Test + public void testCustomPattern() throws Exception { + MockEndpoint result = getMockEndpoint("mock:fooBar"); + template.sendBody("direct:fooBar", "bar foobar bar -- barbarfoobarfoobar -- barbar"); + + result.expectedMessageCount(1); + result.assertIsSatisfied(); + Assert.assertEquals( + "-- barbarfoobarfoobar --", + result.getExchanges().get(0).getIn().getBody(Map.class).get("fooBar")); + } + + @Override + protected AbstractXmlApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/component/grok/SpringGrokDataFormatTest.xml"); + } + +} diff --git a/components/camel-grok/src/test/resources/log4j2.properties b/components/camel-grok/src/test/resources/log4j2.properties new file mode 100644 index 0000000..53e9a0a --- /dev/null +++ b/components/camel-grok/src/test/resources/log4j2.properties @@ -0,0 +1,27 @@ +## --------------------------------------------------------------------------- +## 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-grok-test.log +appender.file.layout.type=PatternLayout +appender.file.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +appender.out.type=Console +appender.out.name=out +appender.out.layout.type=PatternLayout +appender.out.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level=INFO +rootLogger.appenderRef.file.ref=file diff --git a/components/camel-grok/src/test/resources/org/apache/camel/component/grok/SpringGrokDataFormatTest.xml b/components/camel-grok/src/test/resources/org/apache/camel/component/grok/SpringGrokDataFormatTest.xml new file mode 100644 index 0000000..27d04ee --- /dev/null +++ b/components/camel-grok/src/test/resources/org/apache/camel/component/grok/SpringGrokDataFormatTest.xml @@ -0,0 +1,70 @@ +<?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:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://www.springframework.org/schema/beans" + 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="myCustomPatternBean" class="org.apache.camel.component.grok.GrokPattern"> + <constructor-arg value="FOOBAR"/> + <constructor-arg value="foo|bar"/> + </bean> + + <bean id="myAnotherCustomPatternBean" class="org.apache.camel.component.grok.GrokPattern"> + <constructor-arg value="FOOBAR_WITH_PREFIX_AND_SUFFIX"/> + <constructor-arg value="-- %{FOOBAR}+ --"/> + </bean> + + <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> + + <!-- we define the custom grok data formats to be used --> + <dataFormats> + <grok id="grokIP" pattern="%{IP:ip}"/> + </dataFormats> + + <route> + <from uri="direct:ipCustom"/> + <unmarshal> + <custom ref="grokIP"/> + </unmarshal> + <to uri="mock:ipCustom"/> + </route> + + <route> + <from uri="direct:ip"/> + <unmarshal> + <grok pattern="%{IP:ip}"/> + </unmarshal> + <to uri="mock:ip"/> + </route> + + <route> + <from uri="direct:fooBar"/> + <unmarshal> + <grok pattern="%{FOOBAR_WITH_PREFIX_AND_SUFFIX:fooBar}"/> + </unmarshal> + <to uri="mock:fooBar"/> + </route> + + </camelContext> + +</beans> diff --git a/components/camel-grok/src/test/resources/org/apache/camel/component/grok/data/access_log b/components/camel-grok/src/test/resources/org/apache/camel/component/grok/data/access_log new file mode 100644 index 0000000..4b20349 --- /dev/null +++ b/components/camel-grok/src/test/resources/org/apache/camel/component/grok/data/access_log @@ -0,0 +1,5 @@ +64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846 +64.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4523 +64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291 +64.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 7352 +64.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253 \ No newline at end of file diff --git a/components/pom.xml b/components/pom.xml index d49c9b3..ded9f83 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -180,6 +180,7 @@ <module>camel-google-pubsub</module> <module>camel-gora</module> <module>camel-grape</module> + <module>camel-grok</module> <module>camel-grpc</module> <module>camel-gson</module> <module>camel-guava-eventbus</module> diff --git a/components/readme.adoc b/components/readme.adoc index 2a619d6..1bb9eee 100644 --- a/components/readme.adoc +++ b/components/readme.adoc @@ -902,7 +902,7 @@ Number of Components: 296 in 233 JAR artifacts (0 deprecated) ==== Data Formats // dataformats: START -Number of Data Formats: 44 in 36 JAR artifacts (0 deprecated) +Number of Data Formats: 45 in 37 JAR artifacts (0 deprecated) [width="100%",cols="4,1,5",options="header"] |=== @@ -938,6 +938,8 @@ Number of Data Formats: 44 in 36 JAR artifacts (0 deprecated) | link:camel-flatpack/src/main/docs/flatpack-dataformat.adoc[Flatpack] (camel-flatpack) | 2.1 | The Flatpack data format is used for working with flat payloads (such as CSV, delimited, or fixed length formats). +| link:camel-grok/src/main/docs/grok-dataformat.adoc[Grok] (camel-grok) | 3.0 | The Grok data format is used for unmarshalling unstructured data to objects using Logstash based Grok patterns. + | link:camel-zip-deflater/src/main/docs/gzipdeflater-dataformat.adoc[GZip Deflater] (camel-zip-deflater) | 2.0 | The GZip data format is a message compression and de-compression format (which works with the popular gzip/gunzip tools). | link:camel-hl7/src/main/docs/hl7-dataformat.adoc[HL7] (camel-hl7) | 2.0 | The HL7 data format can be used to marshal or unmarshal HL7 (Health Care) model objects. diff --git a/core/camel-core/src/main/java/org/apache/camel/builder/DataFormatClause.java b/core/camel-core/src/main/java/org/apache/camel/builder/DataFormatClause.java index 1f081c9..928d429 100644 --- a/core/camel-core/src/main/java/org/apache/camel/builder/DataFormatClause.java +++ b/core/camel-core/src/main/java/org/apache/camel/builder/DataFormatClause.java @@ -35,6 +35,7 @@ import org.apache.camel.model.dataformat.CsvDataFormat; import org.apache.camel.model.dataformat.CustomDataFormat; import org.apache.camel.model.dataformat.FhirJsonDataFormat; import org.apache.camel.model.dataformat.FhirXmlDataFormat; +import org.apache.camel.model.dataformat.GrokDataFormat; import org.apache.camel.model.dataformat.GzipDataFormat; import org.apache.camel.model.dataformat.HL7DataFormat; import org.apache.camel.model.dataformat.IcalDataFormat; @@ -247,6 +248,15 @@ public class DataFormatClause<T extends ProcessorDefinition<?>> { } /** + * Uses the Grok data format + */ + public T grok(String pattern) { + GrokDataFormat grokDataFormat = new GrokDataFormat(); + grokDataFormat.setPattern(pattern); + return dataFormat(grokDataFormat); + } + + /** * Uses the GZIP deflater data format */ public T gzipDeflater() { diff --git a/core/camel-core/src/main/java/org/apache/camel/model/UnmarshalDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/UnmarshalDefinition.java index 6c90046..16ce56e 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/UnmarshalDefinition.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/UnmarshalDefinition.java @@ -36,6 +36,7 @@ import org.apache.camel.model.dataformat.CustomDataFormat; import org.apache.camel.model.dataformat.FhirJsonDataFormat; import org.apache.camel.model.dataformat.FhirXmlDataFormat; import org.apache.camel.model.dataformat.FlatpackDataFormat; +import org.apache.camel.model.dataformat.GrokDataFormat; import org.apache.camel.model.dataformat.GzipDataFormat; import org.apache.camel.model.dataformat.HL7DataFormat; import org.apache.camel.model.dataformat.IcalDataFormat; @@ -87,6 +88,7 @@ public class UnmarshalDefinition extends NoOutputDefinition<UnmarshalDefinition> @XmlElement(required = false, name = "fhirJson", type = FhirJsonDataFormat.class), @XmlElement(required = false, name = "fhirXml", type = FhirXmlDataFormat.class), @XmlElement(required = false, name = "flatpack", type = FlatpackDataFormat.class), + @XmlElement(required = false, name = "grok", type = GrokDataFormat.class), @XmlElement(required = false, name = "gzip", type = GzipDataFormat.class), @XmlElement(required = false, name = "hl7", type = HL7DataFormat.class), @XmlElement(required = false, name = "ical", type = IcalDataFormat.class), diff --git a/core/camel-core/src/main/java/org/apache/camel/model/dataformat/DataFormatsDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/dataformat/DataFormatsDefinition.java index 139173c..d874eb1 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/dataformat/DataFormatsDefinition.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/dataformat/DataFormatsDefinition.java @@ -53,6 +53,7 @@ public class DataFormatsDefinition { @XmlElement(required = false, name = "fhirJson", type = FhirJsonDataFormat.class), @XmlElement(required = false, name = "fhirXml", type = FhirXmlDataFormat.class), @XmlElement(required = false, name = "flatpack", type = FlatpackDataFormat.class), + @XmlElement(required = false, name = "grok", type = GrokDataFormat.class), @XmlElement(required = false, name = "gzip", type = GzipDataFormat.class), @XmlElement(required = false, name = "hl7", type = HL7DataFormat.class), @XmlElement(required = false, name = "ical", type = IcalDataFormat.class), diff --git a/core/camel-core/src/main/java/org/apache/camel/model/dataformat/GrokDataFormat.java b/core/camel-core/src/main/java/org/apache/camel/model/dataformat/GrokDataFormat.java new file mode 100644 index 0000000..b490759 --- /dev/null +++ b/core/camel-core/src/main/java/org/apache/camel/model/dataformat/GrokDataFormat.java @@ -0,0 +1,104 @@ +/* + * 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.model.dataformat; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.camel.model.DataFormatDefinition; +import org.apache.camel.spi.Metadata; + +/** + * The Grok data format is used for unmarshalling unstructured data to objects using Logstash based Grok patterns. + */ +@Metadata(label = "dataformat,transformation", title = "Grok", firstVersion = "3.0.0") +@XmlRootElement(name = "grok") +@XmlAccessorType(XmlAccessType.FIELD) +public class GrokDataFormat extends DataFormatDefinition { + @XmlAttribute(required = true) + @Metadata + private String pattern; + + @XmlAttribute + @Metadata(defaultValue = "false") + private Boolean flattened = false; + + @XmlAttribute + @Metadata(defaultValue = "true") + private Boolean allowMultipleMatchesPerLine = true; + + @XmlAttribute + @Metadata(defaultValue = "false") + private Boolean namedOnly = false; + + public GrokDataFormat() { + super("grok"); + } + + public String getPattern() { + return pattern; + } + + /** + * The grok pattern to match lines of input + */ + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public boolean isFlattened() { + return flattened; + } + + /** + * Turns on flattened mode. In flattened mode the exception is thrown when there are multiple pattern matches with same key. + */ + public void setFlattened(boolean flattened) { + this.flattened = flattened; + } + + public boolean isAllowMultipleMatchesPerLine() { + return allowMultipleMatchesPerLine; + } + + + /** + * If false, every line of input is matched for pattern only once. + * Otherwise the line can be scanned multiple times when non-terminal pattern is used. + */ + public void setAllowMultipleMatchesPerLine(boolean allowMultipleMatchesPerLine) { + this.allowMultipleMatchesPerLine = allowMultipleMatchesPerLine; + } + + public boolean isNamedOnly() { + return namedOnly; + } + + /** + * Whether to capture named expressions only or not (i.e. %{IP:ip} but not ${IP}) + */ + public void setNamedOnly(boolean namedOnly) { + this.namedOnly = namedOnly; + } + + @Override + public String toString() { + return "GrokDataFormat[" + pattern + ']'; + } +} diff --git a/core/camel-core/src/main/java/org/apache/camel/model/transformer/DataFormatTransformerDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/transformer/DataFormatTransformerDefinition.java index 4172779..25ba144 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/transformer/DataFormatTransformerDefinition.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/transformer/DataFormatTransformerDefinition.java @@ -37,6 +37,7 @@ import org.apache.camel.model.dataformat.CustomDataFormat; import org.apache.camel.model.dataformat.FhirJsonDataFormat; import org.apache.camel.model.dataformat.FhirXmlDataFormat; import org.apache.camel.model.dataformat.FlatpackDataFormat; +import org.apache.camel.model.dataformat.GrokDataFormat; import org.apache.camel.model.dataformat.GzipDataFormat; import org.apache.camel.model.dataformat.HL7DataFormat; import org.apache.camel.model.dataformat.IcalDataFormat; @@ -92,6 +93,7 @@ public class DataFormatTransformerDefinition extends TransformerDefinition { @XmlElement(required = false, name = "fhirJson", type = FhirJsonDataFormat.class), @XmlElement(required = false, name = "fhirXml", type = FhirXmlDataFormat.class), @XmlElement(required = false, name = "flatpack", type = FlatpackDataFormat.class), + @XmlElement(required = false, name = "grok", type = GrokDataFormat.class), @XmlElement(required = false, name = "gzip", type = GzipDataFormat.class), @XmlElement(required = false, name = "hl7", type = HL7DataFormat.class), @XmlElement(required = false, name = "ical", type = IcalDataFormat.class), diff --git a/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java b/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java index d47eedb..b387e0e 100644 --- a/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java +++ b/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java @@ -39,6 +39,7 @@ import org.apache.camel.model.dataformat.FhirDataformat; import org.apache.camel.model.dataformat.FhirJsonDataFormat; import org.apache.camel.model.dataformat.FhirXmlDataFormat; import org.apache.camel.model.dataformat.FlatpackDataFormat; +import org.apache.camel.model.dataformat.GrokDataFormat; import org.apache.camel.model.dataformat.GzipDataFormat; import org.apache.camel.model.dataformat.HL7DataFormat; import org.apache.camel.model.dataformat.IcalDataFormat; @@ -91,6 +92,7 @@ public abstract class DataFormatReifier<T extends DataFormatDefinition> { map.put(FhirJsonDataFormat.class, FhirJsonDataFormatReifier::new); map.put(FhirXmlDataFormat.class, FhirXmlDataFormatReifier::new); map.put(FlatpackDataFormat.class, FlatpackDataFormatReifier::new); + map.put(GrokDataFormat.class, GrokDataFormatReifier::new); map.put(GzipDataFormat.class, GzipDataFormatReifier::new); map.put(HL7DataFormat.class, HL7DataFormatReifier::new); map.put(IcalDataFormat.class, IcalDataFormatReifier::new); diff --git a/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/GrokDataFormatReifier.java b/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/GrokDataFormatReifier.java new file mode 100644 index 0000000..aed2a90 --- /dev/null +++ b/core/camel-core/src/main/java/org/apache/camel/reifier/dataformat/GrokDataFormatReifier.java @@ -0,0 +1,40 @@ +/* + * 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.reifier.dataformat; + +import org.apache.camel.CamelContext; +import org.apache.camel.model.DataFormatDefinition; +import org.apache.camel.model.dataformat.GrokDataFormat; +import org.apache.camel.model.dataformat.SyslogDataFormat; +import org.apache.camel.spi.DataFormat; +import org.apache.camel.util.ObjectHelper; + +public class GrokDataFormatReifier extends DataFormatReifier<GrokDataFormat> { + + public GrokDataFormatReifier(DataFormatDefinition definition) { + super((GrokDataFormat) definition); + } + + @Override + protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) { + ObjectHelper.notNull(definition.getPattern(), "pattern"); + setProperty(camelContext, dataFormat, "pattern", definition.getPattern()); + setProperty(camelContext, dataFormat, "flattened", definition.isFlattened()); + setProperty(camelContext, dataFormat, "allowMultipleMatchesPerLine", definition.isAllowMultipleMatchesPerLine()); + setProperty(camelContext, dataFormat, "namedOnly", definition.isNamedOnly()); + } +} diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc index b95c954..42afa10 100644 --- a/docs/components/modules/ROOT/nav.adoc +++ b/docs/components/modules/ROOT/nav.adoc @@ -125,6 +125,7 @@ * xref:google-sheets-stream-component.adoc[Google Sheets Stream Component] * xref:gora-component.adoc[Gora Component] * xref:grape-component.adoc[Grape Component] +* xref:grok-dataformat.adoc[Grok DataFormat] * xref:groovy-language.adoc[Groovy Language] * xref:grpc-component.adoc[gRPC Component] * xref:json-gson-dataformat.adoc[JSon GSon DataFormat] diff --git a/docs/components/modules/ROOT/pages/grok-dataformat.adoc b/docs/components/modules/ROOT/pages/grok-dataformat.adoc new file mode 100644 index 0000000..e160960 --- /dev/null +++ b/docs/components/modules/ROOT/pages/grok-dataformat.adoc @@ -0,0 +1,138 @@ +[[grok-dataformat]] +== Grok DataFormat + +*Available as of Camel version 3.0* + +This component provides dataformat for processing inputs with grok patterns. +Grok patterns are used to process unstructured data into structured objects - `List<Map<String, Object>`. + +This component is based on https://github.com/thekrakken/java-grok[Java Grok library] + +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-grok</artifactId> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +------------------------------------------------------------ + +### Basic usage + +Extract all IP adresses from input +[source,java] +-------------------------------------------------------------------------------- +from("direct:in") + .unmarshal().grok("%{IP:ip}") + .to("log:out"); +-------------------------------------------------------------------------------- + +Parse Apache logs and process only 4xx responses +[source,java] +-------------------------------------------------------------------------------- +from("file://apacheLogs") + .unmarshal().grok("%{COMBINEDAPACHELOG") + .split(body()).filter(simple("${body[response]} starts with '4'")) + .to("log:4xx") +-------------------------------------------------------------------------------- + +### Predefined patterns + +This component comes with predefined patterns, which are based on Logstash patterns. +Full list can be found at https://github.com/thekrakken/java-grok/tree/master/src/main/resources/patterns[Java Grok repository] + +### Custom patterns + +Camel Grok DataFormat supports plugable patterns, which are auto loaded from Camel Registry. +You can register patterns with Java DSL and Spring DSL + +Spring DSL: +[source,xml] +-------------------------------------------------------------------------------- +<beans> + <bean id="myCustomPatternBean" class="org.apache.camel.component.grok.GrokPattern"> + <constructor-arg value="FOOBAR"/> + <constructor-arg value="foo|bar"/> + </bean> +<beans> +<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> + <route> + <from uri="direct:in"/> + <unmarshal> + <grok pattern="%{FOOBAR:fooBar}"/> + </unmarshal> + <to uri="log:out"/> + </route> +</camelContext> +-------------------------------------------------------------------------------- + +Java DSL: +[source,java] +-------------------------------------------------------------------------------- +public class MyRouteBuilder extends RouteBuilder { + + @Override + public void configure() throws Exception { + bindToRegistry("myCustomPatternBean", new GrokPattern("FOOBAR", "foo|bar")); + + from("direct:in") + .unmarshal().grok("%{FOOBAR:fooBar}") + .to("log:out"); + } +} +-------------------------------------------------------------------------------- + + +### Grok Dataformat Options + +// dataformat options: START +The Grok dataformat supports 5 options, which are listed below. + + + +[width="100%",cols="2s,1m,1m,6",options="header"] +|=== +| Name | Default | Java Type | Description +| pattern | | String | The grok pattern to match lines of input +| flattened | false | Boolean | Turns on flattened mode. In flattened mode the exception is thrown when there are multiple pattern matches with same key. +| allowMultipleMatchesPerLine | true | Boolean | If false, every line of input is matched for pattern only once. Otherwise the line can be scanned multiple times when non-terminal pattern is used. +| namedOnly | false | Boolean | Whether to capture named expressions only or not (i.e. %{IP:ip} but not ${IP}) +| contentTypeHeader | false | Boolean | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc. +|=== +// dataformat 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-grok-starter</artifactId> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +---- + + +The component supports 6 options, which are listed below. + + + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *camel.dataformat.grok.allow-multiple-matches-per-line* | If false, every line of input is matched for pattern only once. Otherwise the line can be scanned multiple times when non-terminal pattern is used. | true | Boolean +| *camel.dataformat.grok.content-type-header* | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc. | false | Boolean +| *camel.dataformat.grok.enabled* | Whether to enable auto configuration of the grok data format. This is enabled by default. | | Boolean +| *camel.dataformat.grok.flattened* | Turns on flattened mode. In flattened mode the exception is thrown when there are multiple pattern matches with same key. | false | Boolean +| *camel.dataformat.grok.named-only* | Whether to capture named expressions only or not (i.e. %{IP:ip} but not ${IP}) | false | Boolean +| *camel.dataformat.grok.pattern* | The grok pattern to match lines of input | | String +|=== +// spring-boot-auto-configure options: END +ND diff --git a/parent/pom.xml b/parent/pom.xml index 6f355dd..9b81a88 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -329,6 +329,7 @@ <java-apns-bundle-version>1.0.0.Beta6_1</java-apns-bundle-version> <java-apns-version>1.0.0.Beta6</java-apns-version> <java-ewah-version>1.1.6</java-ewah-version> + <java-grok-version>0.1.9</java-grok-version> <java-util-version>1.34.0</java-util-version> <java-util-bundle-version>1.34.0_1</java-util-bundle-version> <jnats-version>2.5.0</jnats-version> @@ -1399,6 +1400,11 @@ </dependency> <dependency> <groupId>org.apache.camel</groupId> + <artifactId>camel-grok</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> <artifactId>camel-groovy</artifactId> <version>${project.version}</version> </dependency> @@ -2923,6 +2929,11 @@ </dependency> <dependency> <groupId>org.apache.camel</groupId> + <artifactId>camel-grok-starter</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> <artifactId>camel-groovy-starter</artifactId> <version>${project.version}</version> </dependency> @@ -5124,6 +5135,13 @@ <artifactId>pulsar-client-admin</artifactId> <version>${pulsar-version}</version> </dependency> + + <!-- Optional grok --> + <dependency> + <groupId>io.krakens</groupId> + <artifactId>java-grok</artifactId> + <version>${java-grok-version}</version> + </dependency> </dependencies> </dependencyManagement> diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/pom.xml b/platforms/spring-boot/components-starter/camel-grok-starter/pom.xml new file mode 100644 index 0000000..203a1ff --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/pom.xml @@ -0,0 +1,53 @@ +<?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="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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> + <groupId>org.apache.camel</groupId> + <artifactId>components-starter</artifactId> + <version>3.0.0-SNAPSHOT</version> + </parent> + <artifactId>camel-grok-starter</artifactId> + <packaging>jar</packaging> + <name>Spring-Boot Starter :: Camel :: Grok</name> + <description>Spring-Boot Starter for Camel Grok support</description> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + <version>${spring-boot-version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-grok</artifactId> + <version>${project.version}</version> + </dependency> + <!--START OF GENERATED CODE--> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-core-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring-boot-starter</artifactId> + </dependency> + <!--END OF GENERATED CODE--> + </dependencies> +</project> diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/src/main/java/org/apache/camel/component/grok/springboot/GrokDataFormatAutoConfiguration.java b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/java/org/apache/camel/component/grok/springboot/GrokDataFormatAutoConfiguration.java new file mode 100644 index 0000000..43ff968 --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/java/org/apache/camel/component/grok/springboot/GrokDataFormatAutoConfiguration.java @@ -0,0 +1,128 @@ +/* + * 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.grok.springboot; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Generated; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.component.grok.GrokDataFormat; +import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatCustomizer; +import org.apache.camel.spi.DataFormatFactory; +import org.apache.camel.spi.HasId; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.spring.boot.DataFormatConfigurationProperties; +import org.apache.camel.spring.boot.util.CamelPropertiesHelper; +import org.apache.camel.spring.boot.util.ConditionalOnCamelContextAndAutoConfigurationBeans; +import org.apache.camel.spring.boot.util.GroupCondition; +import org.apache.camel.spring.boot.util.HierarchicalPropertiesEvaluator; +import org.apache.camel.support.IntrospectionSupport; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; + +/** + * Generated by camel-package-maven-plugin - do not edit this file! + */ +@Generated("org.apache.camel.maven.packaging.SpringBootAutoConfigurationMojo") +@Configuration +@Conditional({ConditionalOnCamelContextAndAutoConfigurationBeans.class, + GrokDataFormatAutoConfiguration.GroupConditions.class}) +@AutoConfigureAfter(name = "org.apache.camel.spring.boot.CamelAutoConfiguration") +@EnableConfigurationProperties({DataFormatConfigurationProperties.class, + GrokDataFormatConfiguration.class}) +public class GrokDataFormatAutoConfiguration { + + private static final Logger LOGGER = LoggerFactory + .getLogger(GrokDataFormatAutoConfiguration.class); + @Autowired + private ApplicationContext applicationContext; + @Autowired + private CamelContext camelContext; + @Autowired + private GrokDataFormatConfiguration configuration; + @Autowired(required = false) + private List<DataFormatCustomizer<GrokDataFormat>> customizers; + + static class GroupConditions extends GroupCondition { + public GroupConditions() { + super("camel.dataformat", "camel.dataformat.grok"); + } + } + + @Bean(name = "grok-dataformat-factory") + @ConditionalOnMissingBean(GrokDataFormat.class) + public DataFormatFactory configureGrokDataFormatFactory() throws Exception { + return new DataFormatFactory() { + @Override + public DataFormat newInstance() { + GrokDataFormat dataformat = new GrokDataFormat(); + if (CamelContextAware.class + .isAssignableFrom(GrokDataFormat.class)) { + CamelContextAware contextAware = CamelContextAware.class + .cast(dataformat); + if (contextAware != null) { + contextAware.setCamelContext(camelContext); + } + } + try { + Map<String, Object> parameters = new HashMap<>(); + IntrospectionSupport.getProperties(configuration, + parameters, null, false); + CamelPropertiesHelper.setCamelProperties(camelContext, + dataformat, parameters, false); + } catch (Exception e) { + throw new RuntimeCamelException(e); + } + if (ObjectHelper.isNotEmpty(customizers)) { + for (DataFormatCustomizer<GrokDataFormat> customizer : customizers) { + boolean useCustomizer = (customizer instanceof HasId) + ? HierarchicalPropertiesEvaluator.evaluate( + applicationContext.getEnvironment(), + "camel.dataformat.customizer", + "camel.dataformat.grok.customizer", + ((HasId) customizer).getId()) + : HierarchicalPropertiesEvaluator.evaluate( + applicationContext.getEnvironment(), + "camel.dataformat.customizer", + "camel.dataformat.grok.customizer"); + if (useCustomizer) { + LOGGER.debug( + "Configure dataformat {}, with customizer {}", + dataformat, customizer); + customizer.customize(dataformat); + } + } + } + return dataformat; + } + }; + } +} \ No newline at end of file diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/src/main/java/org/apache/camel/component/grok/springboot/GrokDataFormatConfiguration.java b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/java/org/apache/camel/component/grok/springboot/GrokDataFormatConfiguration.java new file mode 100644 index 0000000..39bfc50 --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/java/org/apache/camel/component/grok/springboot/GrokDataFormatConfiguration.java @@ -0,0 +1,107 @@ +/* + * 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.grok.springboot; + +import javax.annotation.Generated; +import org.apache.camel.spring.boot.DataFormatConfigurationPropertiesCommon; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * The Grok data format is used for unmarshalling unstructured data to objects + * using Logstash based Grok patterns. + * + * Generated by camel-package-maven-plugin - do not edit this file! + */ +@Generated("org.apache.camel.maven.packaging.SpringBootAutoConfigurationMojo") +@ConfigurationProperties(prefix = "camel.dataformat.grok") +public class GrokDataFormatConfiguration + extends + DataFormatConfigurationPropertiesCommon { + + /** + * Whether to enable auto configuration of the grok data format. This is + * enabled by default. + */ + private Boolean enabled; + /** + * The grok pattern to match lines of input + */ + private String pattern; + /** + * Turns on flattened mode. In flattened mode the exception is thrown when + * there are multiple pattern matches with same key. + */ + private Boolean flattened = false; + /** + * If false, every line of input is matched for pattern only once. Otherwise + * the line can be scanned multiple times when non-terminal pattern is used. + */ + private Boolean allowMultipleMatchesPerLine = true; + /** + * Whether to capture named expressions only or not (i.e. %{IP:ip} but not + * ${IP}) + */ + private Boolean namedOnly = false; + /** + * Whether the data format should set the Content-Type header with the type + * from the data format if the data format is capable of doing so. For + * example application/xml for data formats marshalling to XML, or + * application/json for data formats marshalling to JSon etc. + */ + private Boolean contentTypeHeader = false; + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public Boolean getFlattened() { + return flattened; + } + + public void setFlattened(Boolean flattened) { + this.flattened = flattened; + } + + public Boolean getAllowMultipleMatchesPerLine() { + return allowMultipleMatchesPerLine; + } + + public void setAllowMultipleMatchesPerLine( + Boolean allowMultipleMatchesPerLine) { + this.allowMultipleMatchesPerLine = allowMultipleMatchesPerLine; + } + + public Boolean getNamedOnly() { + return namedOnly; + } + + public void setNamedOnly(Boolean namedOnly) { + this.namedOnly = namedOnly; + } + + public Boolean getContentTypeHeader() { + return contentTypeHeader; + } + + public void setContentTypeHeader(Boolean contentTypeHeader) { + this.contentTypeHeader = contentTypeHeader; + } +} \ No newline at end of file diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/LICENSE.txt b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/LICENSE.txt new file mode 100644 index 0000000..43e91eb --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/NOTICE.txt b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/NOTICE.txt new file mode 100644 index 0000000..455e6a5 --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/NOTICE.txt @@ -0,0 +1,11 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Apache Camel distribution. == + ========================================================================= + + This product includes software developed by + The Apache Software Foundation (http://www.apache.org/). + + Please read the different LICENSE files present in the licenses directory of + this distribution. diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/spring.factories b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..4140bba --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,19 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.camel.component.grok.springboot.GrokDataFormatAutoConfiguration diff --git a/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/spring.provides b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/spring.provides new file mode 100644 index 0000000..6fa80f2 --- /dev/null +++ b/platforms/spring-boot/components-starter/camel-grok-starter/src/main/resources/META-INF/spring.provides @@ -0,0 +1,17 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +provides: camel-grok diff --git a/platforms/spring-boot/components-starter/pom.xml b/platforms/spring-boot/components-starter/pom.xml index 71214b6..c8f3397 100644 --- a/platforms/spring-boot/components-starter/pom.xml +++ b/platforms/spring-boot/components-starter/pom.xml @@ -202,6 +202,7 @@ <module>camel-google-sheets-starter</module> <module>camel-gora-starter</module> <module>camel-grape-starter</module> + <module>camel-grok-starter</module> <module>camel-groovy-starter</module> <module>camel-grpc-starter</module> <module>camel-gson-starter</module> diff --git a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml index a8d1739..eab2efe 100644 --- a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml +++ b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml @@ -167,6 +167,11 @@ <version>1.7</version> </dependency> <dependency> + <groupId>io.krakens</groupId> + <artifactId>java-grok</artifactId> + <version>0.1.9</version> + </dependency> + <dependency> <groupId>io.nessus</groupId> <artifactId>nessus-ipfs-client</artifactId> <version>1.0.0.Beta4</version> @@ -1354,6 +1359,16 @@ </dependency> <dependency> <groupId>org.apache.camel</groupId> + <artifactId>camel-grok</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-grok-starter</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> <artifactId>camel-groovy</artifactId> <version>${project.version}</version> </dependency>
