Author: davsclaus
Date: Sat Oct 29 12:37:21 2011
New Revision: 1194882
URL: http://svn.apache.org/viewvc?rev=1194882&view=rev
Log:
CAMEL-4589: Added tokenizer pair to split big XML files in streaming mode, with
low memory footprint.
Added:
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/TokenPairExpressionIterator.java
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenPairIteratorSplitChoicePerformanceTest.java
camel/trunk/camel-core/src/test/java/org/apache/camel/language/XPathSplitChoicePerformanceTest.java
camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java
(with props)
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
camel/trunk/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentTest.java
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.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=1194882&r1=1194881&r2=1194882&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
Sat Oct 29 12:37:21 2011
@@ -39,6 +39,7 @@ import org.apache.camel.component.bean.B
import org.apache.camel.component.properties.PropertiesComponent;
import org.apache.camel.language.bean.BeanLanguage;
import org.apache.camel.model.language.MethodCallExpression;
+import org.apache.camel.processor.TokenPairExpressionIterator;
import org.apache.camel.spi.Language;
import org.apache.camel.support.ExpressionAdapter;
import org.apache.camel.util.ExchangeHelper;
@@ -1024,6 +1025,13 @@ public final class ExpressionBuilder {
}
/**
+ * Returns an {@link TokenPairExpressionIterator} expression
+ */
+ public static Expression tokenizePairExpression(final String startToken,
final String endToken) {
+ return new TokenPairExpressionIterator(startToken, endToken);
+ }
+
+ /**
* Returns a tokenize expression which will tokenize the string with the
* given regex
*/
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=1194882&r1=1194881&r2=1194882&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
Sat Oct 29 12:37:21 2011
@@ -424,6 +424,17 @@ public class ExpressionClause<T> extends
}
/**
+ * Evaluates a token pair expression on the message body
+ *
+ * @param startToken the start token
+ * @param endToken the end token
+ * @return the builder to continue processing the DSL
+ */
+ public T tokenizePair(String startToken, String endToken) {
+ return delegate.tokenizePair(startToken, endToken);
+ }
+
+ /**
* Evaluates an <a href="http://camel.apache.org/xpath.html">XPath
* expression</a>
*
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=1194882&r1=1194881&r2=1194882&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
Sat Oct 29 12:37:21 2011
@@ -471,6 +471,21 @@ public class ExpressionClauseSupport<T>
}
/**
+ * Evaluates a token pair expression on the message body
+ *
+ * @param startToken the start token
+ * @param endToken the end token
+ * @return the builder to continue processing the DSL
+ */
+ public T tokenizePair(String startToken, String endToken) {
+ TokenizerExpression expression = new TokenizerExpression();
+ expression.setToken(startToken);
+ expression.setEndToken(endToken);
+ setExpressionType(expression);
+ return result;
+ }
+
+ /**
* Evaluates an <a href="http://camel.apache.org/xpath.html">XPath
* expression</a>
*
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=1194882&r1=1194881&r2=1194882&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
Sat Oct 29 12:37:21 2011
@@ -26,10 +26,19 @@ import org.apache.camel.util.ObjectHelpe
/**
* A language for tokenizer expressions.
+ * <p/>
+ * This tokenizer language can operator in two modes
+ * <ul>
+ * <li>default - using a single tokenizer</li>
+ * <li>pair - using both start and end tokens</li>
+ * </ul>
+ * The default mode supports the <tt>headerName</tt> and <tt>regex</tt>
options.
+ * Where as the pair mode only supports <tt>token</tt> and <tt>endToken</tt>.
*/
public class TokenizeLanguage implements Language, IsSingleton {
private String token;
+ private String endToken;
private String headerName;
private boolean regex;
@@ -56,6 +65,13 @@ public class TokenizeLanguage implements
return language.createExpression(null);
}
+ public static Expression tokenizePair(String startToken, String endToken) {
+ TokenizeLanguage language = new TokenizeLanguage();
+ language.setToken(startToken);
+ language.setEndToken(endToken);
+ return language.createExpression(null);
+ }
+
public Predicate createPredicate(String expression) {
return
ExpressionToPredicateAdapter.toPredicate(createExpression(expression));
}
@@ -65,6 +81,13 @@ public class TokenizeLanguage implements
*/
public Expression createExpression() {
ObjectHelper.notNull(token, "token");
+
+ // if end token is provided then use the tokenize pair expression
+ if (endToken != null) {
+ return ExpressionBuilder.tokenizePairExpression(token, endToken);
+ }
+
+ // use the regular tokenizer
Expression exp = headerName == null ?
ExpressionBuilder.bodyExpression() :
ExpressionBuilder.headerExpression(headerName);
if (regex) {
return ExpressionBuilder.regexTokenizeExpression(exp, token);
@@ -88,6 +111,14 @@ public class TokenizeLanguage implements
this.token = token;
}
+ public String getEndToken() {
+ return endToken;
+ }
+
+ public void setEndToken(String endToken) {
+ this.endToken = endToken;
+ }
+
public String getHeaderName() {
return headerName;
}
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=1194882&r1=1194881&r2=1194882&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
Sat Oct 29 12:37:21 2011
@@ -26,9 +26,9 @@ import org.apache.camel.Expression;
import org.apache.camel.language.tokenizer.TokenizeLanguage;
/**
- * For expressions and predicates using a body or header tokenizer
+ * For expressions and predicates using a body or header tokenizer.
*
- * @version
+ * @see TokenizeLanguage
*/
@XmlRootElement(name = "tokenize")
@XmlAccessorType(XmlAccessType.FIELD)
@@ -36,6 +36,8 @@ public class TokenizerExpression extends
@XmlAttribute(required = true)
private String token;
@XmlAttribute
+ private String endToken;
+ @XmlAttribute
private String headerName;
@XmlAttribute
private Boolean regex;
@@ -56,6 +58,14 @@ public class TokenizerExpression extends
this.token = token;
}
+ public String getEndToken() {
+ return endToken;
+ }
+
+ public void setEndToken(String endToken) {
+ this.endToken = endToken;
+ }
+
public String getHeaderName() {
return headerName;
}
@@ -76,6 +86,7 @@ public class TokenizerExpression extends
public Expression createExpression(CamelContext camelContext) {
TokenizeLanguage language = new TokenizeLanguage();
language.setToken(token);
+ language.setEndToken(endToken);
language.setHeaderName(headerName);
if (regex != null) {
language.setRegex(regex);
@@ -85,6 +96,10 @@ public class TokenizerExpression extends
@Override
public String toString() {
- return "tokenize{" + (headerName != null ? "header: " + headerName :
"body()") + " using token: " + token + "}";
+ if (endToken != null) {
+ return "tokenize{body() using tokens: " + token + "..." + endToken
+ "}";
+ } else {
+ return "tokenize{" + (headerName != null ? "header: " + headerName
: "body()") + " using token: " + token + "}";
+ }
}
}
\ No newline at end of file
Added:
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/TokenPairExpressionIterator.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/processor/TokenPairExpressionIterator.java?rev=1194882&view=auto
==============================================================================
---
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/TokenPairExpressionIterator.java
(added)
+++
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/TokenPairExpressionIterator.java
Sat Oct 29 12:37:21 2011
@@ -0,0 +1,131 @@
+/**
+ * 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 java.io.InputStream;
+import java.util.Iterator;
+import java.util.Scanner;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * {@link org.apache.camel.Expression} to walk a {@link
org.apache.camel.Message} body
+ * using an {@link Iterator}, which grabs the content between a start and end
token.
+ * <p/>
+ * The message body must be able to convert to {@link InputStream} type which
is used as stream
+ * to access the message body.
+ * <p/>
+ * Can be used to split big XML files
+ */
+public class TokenPairExpressionIterator extends ExpressionAdapter {
+
+ private final String startToken;
+ private final String endToken;
+
+ public TokenPairExpressionIterator(String startToken, String endToken) {
+ this.startToken = startToken;
+ this.endToken = endToken;
+ ObjectHelper.notEmpty(startToken, "startToken");
+ ObjectHelper.notEmpty(endToken, "endToken");
+ }
+
+ @Override
+ public Object evaluate(Exchange exchange) {
+ try {
+ InputStream in =
exchange.getIn().getMandatoryBody(InputStream.class);
+ return new TokenPairIterator(startToken, endToken, in);
+ } catch (InvalidPayloadException e) {
+ exchange.setException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "tokenize[body() using tokens: " + startToken + "..." +
endToken + "]";
+ }
+
+ /**
+ * Iterator to walk the input stream
+ */
+ private static final class TokenPairIterator implements Iterator {
+
+ private final String startToken;
+ private final String endToken;
+ private final Scanner scanner;
+ private Object image;
+
+ private TokenPairIterator(String startToken, String endToken,
InputStream in) {
+ this.startToken = startToken;
+ this.endToken = endToken;
+ // use end token as delimiter
+ this.scanner = new Scanner(in).useDelimiter(endToken);
+ // this iterator will do look ahead as we may have data
+ // after the last end token, which the scanner would find
+ // so we need to be one step ahead of the scanner
+ this.image = hasNext() ? next() : null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ // must look a head
+ boolean answer = scanner.hasNext();
+ if (!answer) {
+ scanner.close();
+ }
+ return answer;
+ }
+
+ @Override
+ public Object next() {
+ Object answer = image;
+ // calculate next
+ image = hasNext() ? getNext() : null;
+
+ if (answer == null) {
+ // first time the image may be null
+ answer = image;
+ }
+ return answer;
+ }
+
+ private Object getNext() {
+ String next = scanner.next();
+
+ // only grab text after the start token
+ if (next != null && next.contains(startToken)) {
+ next = ObjectHelper.after(next, startToken);
+ }
+
+ // include tokens in answer
+ if (next != null) {
+ StringBuilder sb = new StringBuilder();
+ next =
sb.append(startToken).append(next).append(endToken).toString();
+ }
+ return next;
+ }
+
+ @Override
+ public void remove() {
+ // noop
+ }
+ }
+
+}
Modified:
camel/trunk/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentTest.java?rev=1194882&r1=1194881&r2=1194882&view=diff
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentTest.java
(original)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentTest.java
Sat Oct 29 12:37:21 2011
@@ -434,6 +434,37 @@ public class PropertiesComponentTest ext
}
}
+ public void testCache() throws Exception {
+ PropertiesComponent pc = context.getComponent("properties",
PropertiesComponent.class);
+ assertTrue(pc.isCache());
+ assertNotNull(pc);
+
+ for (int i = 0; i < 2000; i++) {
+ String uri = pc.parseUri("{{cool.mock}}:" + i);
+ assertEquals("mock:" + i, uri);
+ }
+ }
+
+ public void testCacheRoute() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .setBody(simple("${properties:cool.mock}${body}"))
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedMessageCount(2000);
+
+ for (int i = 0; i < 2000; i++) {
+ template.sendBody("direct:start", i);
+ }
+
+ assertMockEndpointsSatisfied();
+ }
+
@Override
protected CamelContext createCamelContext() throws Exception {
CamelContext context = super.createCamelContext();
Added:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenPairIteratorSplitChoicePerformanceTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenPairIteratorSplitChoicePerformanceTest.java?rev=1194882&view=auto
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenPairIteratorSplitChoicePerformanceTest.java
(added)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenPairIteratorSplitChoicePerformanceTest.java
Sat Oct 29 12:37:21 2011
@@ -0,0 +1,183 @@
+/**
+ * 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.language;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.slf4j.Logger;
+
+/**
+ *
+ */
+public class TokenPairIteratorSplitChoicePerformanceTest extends
ContextTestSupport {
+
+ private int size = 20 * 1000;
+ private final AtomicInteger tiny = new AtomicInteger();
+ private final AtomicInteger small = new AtomicInteger();
+ private final AtomicInteger med = new AtomicInteger();
+ private final AtomicInteger large = new AtomicInteger();
+ private final StopWatch watch = new StopWatch();
+
+ @Override
+ public void setUp() throws Exception {
+ createDataFile(log, size);
+ super.setUp();
+ }
+
+ public void testDummy() {
+ // this is a manual test
+ }
+
+ public void xxxtestTokenPairPerformanceRoute() throws Exception {
+ NotifyBuilder notify = new
NotifyBuilder(context).whenDone(size).create();
+
+ boolean matches = notify.matches(5, TimeUnit.MINUTES);
+ log.info("Processed file with " + size + " elements in: " +
TimeUtils.printDuration(watch.stop()));
+
+ log.info("Processed " + tiny.get() + " tiny messages");
+ log.info("Processed " + small.get() + " small messages");
+ log.info("Processed " + med.get() + " medium messages");
+ log.info("Processed " + large.get() + " large messages");
+
+ assertEquals((size / 10) * 4, tiny.get());
+ assertEquals((size / 10) * 2, small.get());
+ assertEquals((size / 10) * 3, med.get());
+ assertEquals((size / 10) * 1, large.get());
+
+ assertTrue("Should complete route", matches);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("file:target/data?noop=true")
+ .process(new Processor() {
+ public void process(Exchange exchange) throws
Exception {
+ log.info("Starting to process file");
+ watch.restart();
+ }
+ })
+ .split().tokenizePair("<order>", "</order>").streaming()
+ .choice()
+ .when().xpath("/order/amount < 10")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>3</amount>"));
+
+ int num = tiny.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
tiny messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .when().xpath("/order/amount < 50")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>44</amount>"));
+
+ int num = small.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
small messages: " + xml);
+ log.debug(xml);
+ }
+ }
+ })
+ .when().xpath("/order/amount < 100")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>88</amount>"));
+
+ int num = med.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
medium messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .otherwise()
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>123</amount>"));
+
+ int num = large.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
large messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .end() // choice
+ .end(); // split
+ }
+ };
+ }
+
+ public static void createDataFile(Logger log, int size) throws Exception {
+ deleteDirectory("target/data");
+ createDirectory("target/data");
+
+ log.info("Creating data file ...");
+
+ File file = new File("target/data/data.xml");
+ FileOutputStream fos = new FileOutputStream(file, true);
+ fos.write("<orders>\n".getBytes());
+
+ for (int i = 0; i < size; i++) {
+ fos.write("<order>\n".getBytes());
+ fos.write((" <id>" + i + "</id>\n").getBytes());
+ int num = i % 10;
+ if (num >= 0 && num <= 3) {
+ fos.write(" <amount>3</amount>\n".getBytes());
+ fos.write(" <customerId>333</customerId>\n".getBytes());
+ } else if (num >= 4 && num <= 5) {
+ fos.write(" <amount>44</amount>\n".getBytes());
+ fos.write(" <customerId>444</customerId>\n".getBytes());
+ } else if (num >= 6 && num <= 8) {
+ fos.write(" <amount>88</amount>\n".getBytes());
+ fos.write(" <customerId>888</customerId>\n".getBytes());
+ } else {
+ fos.write(" <amount>123</amount>\n".getBytes());
+ fos.write(" <customerId>123123</customerId>\n".getBytes());
+ }
+ fos.write(" <description>bla bla bla bla bla bla bla bla bla bla
bla bla bla bla bla bla</description>\n".getBytes());
+ fos.write("</order>\n".getBytes());
+ }
+
+ fos.write("</orders>".getBytes());
+ fos.close();
+
+ log.info("Creating data file done.");
+ }
+
+}
Modified:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java?rev=1194882&r1=1194881&r2=1194882&view=diff
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java
(original)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java
Sat Oct 29 12:37:21 2011
@@ -99,4 +99,51 @@ public class TokenizerTest extends Excha
assertEquals(false, lan.isSingleton());
}
+ public void testTokenizePair() throws Exception {
+ Expression exp = TokenizeLanguage.tokenizePair("<person>",
"</person>");
+
+
exchange.getIn().setBody("<persons><person>James</person><person>Claus</person><person>Jonathan</person><person>Hadrian</person></persons>");
+
+ List names = exp.evaluate(exchange, List.class);
+ assertEquals(4, names.size());
+
+ assertEquals("<person>James</person>", names.get(0));
+ assertEquals("<person>Claus</person>", names.get(1));
+ assertEquals("<person>Jonathan</person>", names.get(2));
+ assertEquals("<person>Hadrian</person>", names.get(3));
+ }
+
+ public void testTokenizePairWithNoise() throws Exception {
+ Expression exp = TokenizeLanguage.tokenizePair("<person>",
"</person>");
+
+ exchange.getIn().setBody("<?xml version=\"1.0\"?><!-- bla bla
--><persons>\n<person>James</person>\n<person>Claus</person>\n"
+ + "<!-- more bla bla
--><person>Jonathan</person>\n<person>Hadrian</person>\n</persons> ");
+
+ List names = exp.evaluate(exchange, List.class);
+ assertEquals(4, names.size());
+
+ assertEquals("<person>James</person>", names.get(0));
+ assertEquals("<person>Claus</person>", names.get(1));
+ assertEquals("<person>Jonathan</person>", names.get(2));
+ assertEquals("<person>Hadrian</person>", names.get(3));
+ }
+
+ public void testTokenizePairEmpty() throws Exception {
+ Expression exp = TokenizeLanguage.tokenizePair("<person>",
"</person>");
+
+ exchange.getIn().setBody("<?xml version=\"1.0\"?><!-- bla bla
--><persons></persons> ");
+
+ List names = exp.evaluate(exchange, List.class);
+ assertEquals(0, names.size());
+ }
+
+ public void testTokenizePairNoData() throws Exception {
+ Expression exp = TokenizeLanguage.tokenizePair("<person>",
"</person>");
+
+ exchange.getIn().setBody("");
+
+ List names = exp.evaluate(exchange, List.class);
+ assertNull(names);
+ }
+
}
\ No newline at end of file
Added:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/XPathSplitChoicePerformanceTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/XPathSplitChoicePerformanceTest.java?rev=1194882&view=auto
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/language/XPathSplitChoicePerformanceTest.java
(added)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/language/XPathSplitChoicePerformanceTest.java
Sat Oct 29 12:37:21 2011
@@ -0,0 +1,183 @@
+/**
+ * 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.language;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.slf4j.Logger;
+
+/**
+ *
+ */
+public class XPathSplitChoicePerformanceTest extends ContextTestSupport {
+
+ private int size = 20 * 1000;
+ private final AtomicInteger tiny = new AtomicInteger();
+ private final AtomicInteger small = new AtomicInteger();
+ private final AtomicInteger med = new AtomicInteger();
+ private final AtomicInteger large = new AtomicInteger();
+ private final StopWatch watch = new StopWatch();
+
+ @Override
+ public void setUp() throws Exception {
+ createDataFile(log, size);
+ super.setUp();
+ }
+
+ public void testDummy() {
+ // this is a manual test
+ }
+
+ public void xxTestXPatPerformanceRoute() throws Exception {
+ NotifyBuilder notify = new
NotifyBuilder(context).whenDone(size).create();
+
+ boolean matches = notify.matches(60, TimeUnit.SECONDS);
+ log.info("Processed file with " + size + " elements in: " +
TimeUtils.printDuration(watch.stop()));
+
+ log.info("Processed " + tiny.get() + " tiny messages");
+ log.info("Processed " + small.get() + " small messages");
+ log.info("Processed " + med.get() + " medium messages");
+ log.info("Processed " + large.get() + " large messages");
+
+ assertEquals((size / 10) * 4, tiny.get());
+ assertEquals((size / 10) * 2, small.get());
+ assertEquals((size / 10) * 3, med.get());
+ assertEquals((size / 10) * 1, large.get());
+
+ assertTrue("Should complete route", matches);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("file:target/data?noop=true")
+ .process(new Processor() {
+ public void process(Exchange exchange) throws
Exception {
+ log.info("Starting to process file");
+ watch.restart();
+ }
+ })
+ .split().xpath("/orders/order").streaming()
+ .choice()
+ .when().xpath("/order/amount < 10")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>3</amount>"));
+
+ int num = tiny.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
tiny messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .when().xpath("/order/amount < 50")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>44</amount>"));
+
+ int num = small.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
small messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .when().xpath("/order/amount < 100")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>88</amount>"));
+
+ int num = med.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
medium messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .otherwise()
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>123</amount>"));
+
+ int num = large.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
large messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .end() // choice
+ .end(); // split
+ }
+ };
+ }
+
+ public static void createDataFile(Logger log, int size) throws Exception {
+ deleteDirectory("target/data");
+ createDirectory("target/data");
+
+ log.info("Creating data file ...");
+
+ File file = new File("target/data/data.xml");
+ FileOutputStream fos = new FileOutputStream(file, true);
+ fos.write("<orders>\n".getBytes());
+
+ for (int i = 0; i < size; i++) {
+ fos.write("<order>\n".getBytes());
+ fos.write((" <id>" + i + "</id>\n").getBytes());
+ int num = i % 10;
+ if (num >= 0 && num <= 3) {
+ fos.write(" <amount>3</amount>\n".getBytes());
+ fos.write(" <customerId>333</customerId>\n".getBytes());
+ } else if (num >= 4 && num <= 5) {
+ fos.write(" <amount>44</amount>\n".getBytes());
+ fos.write(" <customerId>444</customerId>\n".getBytes());
+ } else if (num >= 6 && num <= 8) {
+ fos.write(" <amount>88</amount>\n".getBytes());
+ fos.write(" <customerId>888</customerId>\n".getBytes());
+ } else {
+ fos.write(" <amount>123</amount>\n".getBytes());
+ fos.write(" <customerId>123123</customerId>\n".getBytes());
+ }
+ fos.write(" <description>bla bla bla bla bla bla bla bla bla bla
bla bla bla bla bla bla</description>\n".getBytes());
+ fos.write("</order>\n".getBytes());
+ }
+
+ fos.write("</orders>".getBytes());
+ fos.close();
+
+ log.info("Creating data file done.");
+ }
+
+}
Added:
camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java?rev=1194882&view=auto
==============================================================================
---
camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java
(added)
+++
camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java
Sat Oct 29 12:37:21 2011
@@ -0,0 +1,183 @@
+/**
+ * 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.builder.saxon;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+/**
+ *
+ */
+public class XPathSplitChoicePerformanceTest extends CamelTestSupport {
+
+ private int size = 20 * 1000;
+ private final AtomicInteger tiny = new AtomicInteger();
+ private final AtomicInteger small = new AtomicInteger();
+ private final AtomicInteger med = new AtomicInteger();
+ private final AtomicInteger large = new AtomicInteger();
+ private final StopWatch watch = new StopWatch();
+
+ @Override
+ public void setUp() throws Exception {
+ createDataFile(log, size);
+ super.setUp();
+ }
+
+ @Test
+ @Ignore("Manual test")
+ public void testXPathPerformanceRoute() throws Exception {
+ NotifyBuilder notify = new
NotifyBuilder(context).whenDone(size).create();
+
+ boolean matches = notify.matches(60, TimeUnit.SECONDS);
+ log.info("Processed file with " + size + " elements in: " +
TimeUtils.printDuration(watch.stop()));
+
+ log.info("Processed " + tiny.get() + " tiny messages");
+ log.info("Processed " + small.get() + " small messages");
+ log.info("Processed " + med.get() + " medium messages");
+ log.info("Processed " + large.get() + " large messages");
+
+ assertEquals((size / 10) * 4, tiny.get());
+ assertEquals((size / 10) * 2, small.get());
+ assertEquals((size / 10) * 3, med.get());
+ assertEquals((size / 10) * 1, large.get());
+
+ assertTrue("Should complete route", matches);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("file:target/data?noop=true")
+ .process(new Processor() {
+ public void process(Exchange exchange) throws
Exception {
+ log.info("Starting to process file");
+ watch.restart();
+ }
+ })
+ .split().xpath("/orders/order").streaming()
+ .choice()
+ .when().xpath("/order/amount < 10")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>3</amount>"));
+
+ int num = tiny.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
tiny messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .when().xpath("/order/amount < 50")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>44</amount>"));
+
+ int num = small.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
small messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .when().xpath("/order/amount < 100")
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>88</amount>"));
+
+ int num = med.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
medium messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .otherwise()
+ .process(new Processor() {
+ public void process(Exchange exchange)
throws Exception {
+ String xml =
exchange.getIn().getBody(String.class);
+ assertTrue(xml,
xml.contains("<amount>123</amount>"));
+
+ int num = large.incrementAndGet();
+ if (num % 100 == 0) {
+ log.info("Processed " + num + "
large messages");
+ log.debug(xml);
+ }
+ }
+ })
+ .end() // choice
+ .end(); // split
+ }
+ };
+ }
+
+ public static void createDataFile(Logger log, int size) throws Exception {
+ deleteDirectory("target/data");
+ createDirectory("target/data");
+
+ log.info("Creating data file ...");
+
+ File file = new File("target/data/data.xml");
+ FileOutputStream fos = new FileOutputStream(file, true);
+ fos.write("<orders>\n".getBytes());
+
+ for (int i = 0; i < size; i++) {
+ fos.write("<order>\n".getBytes());
+ fos.write((" <id>" + i + "</id>\n").getBytes());
+ int num = i % 10;
+ if (num >= 0 && num <= 3) {
+ fos.write(" <amount>3</amount>\n".getBytes());
+ fos.write(" <customerId>333</customerId>\n".getBytes());
+ } else if (num >= 4 && num <= 5) {
+ fos.write(" <amount>44</amount>\n".getBytes());
+ fos.write(" <customerId>444</customerId>\n".getBytes());
+ } else if (num >= 6 && num <= 8) {
+ fos.write(" <amount>88</amount>\n".getBytes());
+ fos.write(" <customerId>888</customerId>\n".getBytes());
+ } else {
+ fos.write(" <amount>123</amount>\n".getBytes());
+ fos.write(" <customerId>123123</customerId>\n".getBytes());
+ }
+ fos.write(" <description>bla bla bla bla bla bla bla bla bla bla
bla bla bla bla bla bla</description>\n".getBytes());
+ fos.write("</order>\n".getBytes());
+ }
+
+ fos.write("</orders>".getBytes());
+ fos.close();
+
+ log.info("Creating data file done.");
+ }
+
+}
Propchange:
camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathSplitChoicePerformanceTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date