You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2012/05/01 15:38:27 UTC

svn commit: r1332649 - in /camel/trunk: camel-core/src/main/java/org/apache/camel/builder/ camel-core/src/main/java/org/apache/camel/language/tokenizer/ camel-core/src/main/java/org/apache/camel/model/language/ camel-core/src/main/java/org/apache/camel...

Author: davsclaus
Date: Tue May  1 13:38:27 2012
New Revision: 1332649

URL: http://svn.apache.org/viewvc?rev=1332649&view=rev
Log:
CAMEL-5326: Added group option to tokenizer to group N parts together as a combined message. For example to split large files by N line numbers.

Added:
    camel/trunk/camel-core/src/main/java/org/apache/camel/util/GroupIterator.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiLinesTest.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/util/GroupIteratorTest.java
    camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringSplitGroupMultiLinesTest.java
      - copied, changed from r1332534, camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringAOPAfterTest.java
    camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/SplitGroupMultiLinesTest.xml
      - copied, changed from r1332534, camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/aopafter.xml
Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/model/language/TokenizerExpression.java

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java?rev=1332649&r1=1332648&r2=1332649&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java Tue May  1 13:38:27 2012
@@ -23,6 +23,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Scanner;
 import java.util.regex.Pattern;
@@ -45,6 +46,7 @@ import org.apache.camel.support.TokenPai
 import org.apache.camel.support.TokenXMLPairExpressionIterator;
 import org.apache.camel.util.ExchangeHelper;
 import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.GroupIterator;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.OgnlHelper;
@@ -1113,6 +1115,22 @@ public final class ExpressionBuilder {
         };
     }
 
+    public static Expression groupIteratorExpression(final Expression expression, final String token, final int group) {
+        return new ExpressionAdapter() {
+            public Object evaluate(Exchange exchange) {
+                // evaluate expression as iterator
+                Iterator it = expression.evaluate(exchange, Iterator.class);
+                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
+                return new GroupIterator(exchange.getContext(), it, token, group);
+            }
+
+            @Override
+            public String toString() {
+                return "group " + expression + " " + group + " times";
+            }
+        };
+    }
+
     /**
      * Returns a sort expression which will sort the expression with the given comparator.
      * <p/>

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java?rev=1332649&r1=1332648&r2=1332649&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java Tue May  1 13:38:27 2012
@@ -419,6 +419,29 @@ public class ExpressionClause<T> extends
     }
 
     /**
+     * Evaluates a token expression on the message body
+     *
+     * @param token the token
+     * @param regex whether the token is a regular expression or not
+     * @param group to group by the given number
+     * @return the builder to continue processing the DSL
+     */
+    public T tokenize(String token, boolean regex, int group) {
+        return delegate.tokenize(token, regex);
+    }
+
+    /**
+     * Evaluates a token expression on the message body
+     *
+     * @param token the token
+     * @param group to group by the given number
+     * @return the builder to continue processing the DSL
+     */
+    public T tokenize(String token, int group) {
+        return delegate.tokenize(token, group);
+    }
+
+    /**
      * Evaluates a token expression on the given header
      *
      * @param token the token

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java?rev=1332649&r1=1332648&r2=1332649&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java Tue May  1 13:38:27 2012
@@ -448,6 +448,17 @@ public class ExpressionClauseSupport<T> 
      * Evaluates a token expression on the message body
      *
      * @param token the token
+     * @param group to group by the given number
+     * @return the builder to continue processing the DSL
+     */
+    public T tokenize(String token, int group) {
+        return tokenize(token, null, false, group);
+    }
+
+    /**
+     * Evaluates a token expression on the message body
+     *
+     * @param token the token
      * @param regex whether the token is a regular expression or not
      * @return the builder to continue processing the DSL
      */
@@ -456,6 +467,18 @@ public class ExpressionClauseSupport<T> 
     }
 
     /**
+     * Evaluates a token expression on the message body
+     *
+     * @param token the token
+     * @param regex whether the token is a regular expression or not
+     * @param group to group by the given number
+     * @return the builder to continue processing the DSL
+     */
+    public T tokenize(String token, boolean regex, int group) {
+        return tokenize(token, null, regex, group);
+    }
+
+    /**
      * Evaluates a token expression on the given header
      *
      * @param token the token
@@ -484,6 +507,25 @@ public class ExpressionClauseSupport<T> 
     }
 
     /**
+     * Evaluates a token expression on the given header
+     *
+     * @param token the token
+     * @param headerName name of header to tokenize
+     * @param regex whether the token is a regular expression or not
+     * @param group to group by number of parts
+     * @return the builder to continue processing the DSL
+     */
+    public T tokenize(String token, String headerName, boolean regex, int group) {
+        TokenizerExpression expression = new TokenizerExpression();
+        expression.setToken(token);
+        expression.setHeaderName(headerName);
+        expression.setRegex(regex);
+        expression.setGroup(group);
+        setExpressionType(expression);
+        return result;
+    }
+
+    /**
      * Evaluates a token pair expression on the message body
      *
      * @param startToken the start token

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java?rev=1332649&r1=1332648&r2=1332649&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java Tue May  1 13:38:27 2012
@@ -46,6 +46,7 @@ public class TokenizeLanguage implements
     private boolean regex;
     private boolean xml;
     private boolean includeTokens;
+    private int group;
 
     public static Expression tokenize(String token) {
         return tokenize(token, false);
@@ -104,12 +105,20 @@ public class TokenizeLanguage implements
         }
 
         // use the regular tokenizer
+        Expression answer;
         Expression exp = headerName == null ? ExpressionBuilder.bodyExpression() : ExpressionBuilder.headerExpression(headerName);
         if (regex) {
-            return ExpressionBuilder.regexTokenizeExpression(exp, token);
+            answer = ExpressionBuilder.regexTokenizeExpression(exp, token);
         } else {
-            return ExpressionBuilder.tokenizeExpression(exp, token);
+            answer = ExpressionBuilder.tokenizeExpression(exp, token);
         }
+
+        // if group then wrap answer in group expression
+        if (group > 0) {
+            answer = ExpressionBuilder.groupIteratorExpression(answer, token, group);
+        }
+
+        return answer;
     }
 
     public Expression createExpression(String expression) {
@@ -175,6 +184,14 @@ public class TokenizeLanguage implements
         this.includeTokens = includeTokens;
     }
 
+    public int getGroup() {
+        return group;
+    }
+
+    public void setGroup(int group) {
+        this.group = group;
+    }
+
     public boolean isSingleton() {
         return false;
     }

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/model/language/TokenizerExpression.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/model/language/TokenizerExpression.java?rev=1332649&r1=1332648&r2=1332649&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/model/language/TokenizerExpression.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/model/language/TokenizerExpression.java Tue May  1 13:38:27 2012
@@ -49,6 +49,8 @@ public class TokenizerExpression extends
     private Boolean xml;
     @XmlAttribute
     private Boolean includeTokens;
+    @XmlAttribute
+    private Integer group;
 
     public TokenizerExpression() {
     }
@@ -114,8 +116,21 @@ public class TokenizerExpression extends
         this.includeTokens = includeTokens;
     }
 
+    public Integer getGroup() {
+        return group;
+    }
+
+    public void setGroup(Integer group) {
+        this.group = group;
+    }
+
     @Override
     public Expression createExpression(CamelContext camelContext) {
+        // special for new line tokens, if defined from XML then its 2 characters, so we replace that back to a single char
+        if (token.startsWith("\\n")) {
+            token = '\n' + token.substring(2);
+        }
+
         TokenizeLanguage language = new TokenizeLanguage();
         language.setToken(token);
         language.setEndToken(endToken);
@@ -130,6 +145,12 @@ public class TokenizerExpression extends
         if (includeTokens != null) {
             language.setIncludeTokens(includeTokens);
         }
+        if (group != null) {
+            if (group <= 0) {
+                throw new IllegalArgumentException("Group must be a positive number, was: " + group);
+            }
+            language.setGroup(group);
+        }
         return language.createExpression();
     }
 

Added: camel/trunk/camel-core/src/main/java/org/apache/camel/util/GroupIterator.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/util/GroupIterator.java?rev=1332649&view=auto
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/util/GroupIterator.java (added)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/util/GroupIterator.java Tue May  1 13:38:27 2012
@@ -0,0 +1,142 @@
+/**
+ * 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.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Scanner;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.NoTypeConversionAvailableException;
+
+/**
+ * Group based {@link Iterator} which groups the given {@link Iterator} a number of times
+ * and then return a combined response as a String.
+ * <p/>
+ * This implementation uses as internal byte array buffer, to combine the response.
+ * The token is inserted between the individual parts.
+ * <p/>
+ * For example if you group by new line, then a new line token is inserted between the lines.
+ */
+public final class GroupIterator implements Iterator, Closeable {
+
+    private final CamelContext camelContext;
+    private final Iterator it;
+    private final String token;
+    private final int group;
+    private boolean closed;
+    private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+    /**
+     * Creates a new group iterator
+     *
+     * @param camelContext  the camel context
+     * @param it            the iterator to group
+     * @param token         then token used to separate between the parts, use <tt>null</tt> to not add the token
+     * @param group         number of parts to group together
+     * @throws IllegalArgumentException is thrown if group is not a positive number
+     */
+    public GroupIterator(CamelContext camelContext, Iterator it, String token, int group) {
+        this.camelContext = camelContext;
+        this.it = it;
+        this.token = token;
+        this.group = group;
+        if (group <= 0) {
+            throw new IllegalArgumentException("Group must be a positive number, was: " + group);
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (it instanceof Closeable) {
+            IOHelper.close((Closeable) it);
+        } else if (it instanceof Scanner) {
+            // special for Scanner as it does not implement Closeable
+            ((Scanner) it).close();
+        }
+        // close the buffer as well
+        bos.close();
+        // we are now closed
+        closed = true;
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (closed) {
+            return false;
+        }
+
+        boolean answer = it.hasNext();
+        if (!answer) {
+            // auto close
+            try {
+                close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return answer;
+    }
+
+    @Override
+    public Object next() {
+        try {
+            return doNext();
+        } catch (Exception e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+    }
+
+    private Object doNext() throws IOException, NoTypeConversionAvailableException {
+        int count = 0;
+        Object data = "";
+        while (count < group && it.hasNext()) {
+            data = it.next();
+
+            // include token in between
+            if (data != null && count > 0 && token != null) {
+                bos.write(token.getBytes());
+            }
+            if (data instanceof InputStream) {
+                InputStream is = (InputStream) data;
+                IOHelper.copy(is, bos);
+            } else if (data instanceof byte[]) {
+                byte[] bytes = (byte[]) data;
+                bos.write(bytes);
+            } else if (data != null) {
+                // convert to input stream
+                InputStream is = camelContext.getTypeConverter().mandatoryConvertTo(InputStream.class, data);
+                IOHelper.copy(is, bos);
+            }
+
+            count++;
+        }
+
+        // prepare and return answer as String
+        String answer = bos.toString();
+        bos.reset();
+        return answer;
+    }
+
+    @Override
+    public void remove() {
+        it.remove();
+    }
+}

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiLinesTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiLinesTest.java?rev=1332649&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiLinesTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiLinesTest.java Tue May  1 13:38:27 2012
@@ -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.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+
+/**
+ *
+ */
+public class SplitGroupMultiLinesTest extends ContextTestSupport {
+
+    public void testSplitMultiLines() throws Exception {
+        getMockEndpoint("mock:group").expectedBodiesReceived("ABC\nDEF\nGHI", "JKL\nMN");
+
+        template.sendBody("direct:start", "ABC\nDEF\nGHI\nJKL\nMN");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // START SNIPPET: e1
+                from("direct:start")
+                    // split by new line and group by 3
+                    .split().tokenize("\n", 3).streaming()
+                        .to("mock:group");
+                // END SNIPPET: e1
+            }
+        };
+    }
+
+}

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/util/GroupIteratorTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/util/GroupIteratorTest.java?rev=1332649&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/util/GroupIteratorTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/util/GroupIteratorTest.java Tue May  1 13:38:27 2012
@@ -0,0 +1,59 @@
+/**
+ * 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.util;
+
+import java.util.Scanner;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.TestSupport;
+import org.apache.camel.impl.DefaultCamelContext;
+
+/**
+ *
+ */
+public class GroupIteratorTest extends TestSupport {
+
+    private CamelContext context;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        context = new DefaultCamelContext();
+        context.start();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        context.stop();
+        super.tearDown();
+    }
+
+    public void testGroupIterator() throws Exception {
+        String s = "ABC\nDEF\nGHI\nJKL\nMNO\nPQR\nSTU\nVW";
+        Scanner scanner = new Scanner(s);
+        scanner.useDelimiter("\n");
+
+        GroupIterator gi = new GroupIterator(context, scanner, "\n", 3);
+
+        assertTrue(gi.hasNext());
+        assertEquals("ABC\nDEF\nGHI", gi.next());
+        assertEquals("JKL\nMNO\nPQR", gi.next());
+        assertEquals("STU\nVW", gi.next());
+        assertFalse(gi.hasNext());
+    }
+
+}

Copied: camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringSplitGroupMultiLinesTest.java (from r1332534, camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringAOPAfterTest.java)
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringSplitGroupMultiLinesTest.java?p2=camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringSplitGroupMultiLinesTest.java&p1=camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringAOPAfterTest.java&r1=1332534&r2=1332649&rev=1332649&view=diff
==============================================================================
--- camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringAOPAfterTest.java (original)
+++ camel/trunk/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringSplitGroupMultiLinesTest.java Tue May  1 13:38:27 2012
@@ -17,15 +17,16 @@
 package org.apache.camel.spring.processor;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.processor.AOPAfterTest;
+import org.apache.camel.processor.SplitGroupMultiLinesTest;
+
 import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
 
 /**
  * @version 
  */
-public class SpringAOPAfterTest extends AOPAfterTest {
+public class SpringSplitGroupMultiLinesTest extends SplitGroupMultiLinesTest {
 
     protected CamelContext createCamelContext() throws Exception {
-        return createSpringCamelContext(this, "org/apache/camel/spring/processor/aopafter.xml");
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/SplitGroupMultiLinesTest.xml");
     }
 }
\ No newline at end of file

Copied: camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/SplitGroupMultiLinesTest.xml (from r1332534, camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/aopafter.xml)
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/SplitGroupMultiLinesTest.xml?p2=camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/SplitGroupMultiLinesTest.xml&p1=camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/aopafter.xml&r1=1332534&r2=1332649&rev=1332649&view=diff
==============================================================================
--- camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/aopafter.xml (original)
+++ camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/SplitGroupMultiLinesTest.xml Tue May  1 13:38:27 2012
@@ -22,16 +22,17 @@
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
     ">
 
-    <!-- START SNIPPET: e1 -->
-    <camelContext xmlns="http://camel.apache.org/schema/spring">
-        <route>
-            <from uri="direct:start"/>
-            <aop afterUri="mock:after">
-                <transform><constant>Bye World</constant></transform>
-                <to uri="mock:result"/>
-            </aop>
-        </route>
-    </camelContext>
-    <!-- END SNIPPET: e1 -->
+  <!-- START SNIPPET: e1 -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <route>
+      <from uri="direct:start"/>
+      <split streaming="true">
+        <!-- split by new line and group by 3 -->
+        <tokenize token="\n" group="3"/>
+        <to uri="mock:group"/>
+      </split>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: e1 -->
 
 </beans>