Moved over camel route coverage maven plugin to master branch.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/f015f7b0 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/f015f7b0 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/f015f7b0 Branch: refs/heads/master Commit: f015f7b0d3a33712454637f2d9d8f24a3743adca Parents: 9ffb254 Author: Claus Ibsen <davscl...@apache.org> Authored: Thu Oct 12 19:20:05 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Thu Oct 12 19:21:23 2017 +0200 ---------------------------------------------------------------------- .../mbean/RouteCoverageXmlParser.java | 17 +- .../test/spring/CamelAnnotationsHandler.java | 25 + .../CamelSpringBootExecutionListener.java | 11 +- .../CamelSpringDelegatingTestContextLoader.java | 13 + .../spring/CamelSpringTestContextLoader.java | 45 +- ...gTestContextLoaderTestExecutionListener.java | 3 +- .../test/spring/CamelSpringTestHelper.java | 14 +- .../camel/test/spring/EnableRouteCoverage.java | 39 ++ .../test/spring/RouteCoverageEventNotifier.java | 98 ++++ ...ringRouteProcessorDumpRouteCoverageTest.java | 55 +++ .../test/spring/CamelSpringRunnerPlainTest.java | 5 + .../camel/test/junit4/CamelTestSupport.java | 4 +- examples/camel-example-spring-boot-xml/pom.xml | 151 ++++++ .../camel-example-spring-boot-xml/readme.adoc | 41 ++ .../src/main/java/sample/camel/SampleBean.java | 38 ++ .../sample/camel/SampleCamelApplication.java | 40 ++ .../src/main/resources/application.properties | 46 ++ .../src/main/resources/my-camel.xml | 40 ++ .../java/sample/camel/FooApplicationTest.java | 52 ++ .../camel/SampleCamelApplicationTest.java | 49 ++ examples/camel-example-spring-boot/pom.xml | 12 + .../java/sample/camel/SampleCamelRouter.java | 7 +- .../java/sample/camel/FooApplicationTest.java | 52 ++ .../camel/SampleCamelApplicationTest.java | 2 + examples/pom.xml | 1 + tooling/camel-route-parser/pom.xml | 7 + .../apache/camel/parser/RouteBuilderParser.java | 39 ++ .../org/apache/camel/parser/XmlRouteParser.java | 57 +++ .../apache/camel/parser/graph/RenderRoute.java | 70 +++ .../helper/CamelJavaTreeParserHelper.java | 478 +++++++++++++++++++ .../parser/helper/CamelXmlTreeParserHelper.java | 134 ++++++ .../parser/helper/RouteCoverageHelper.java | 115 +++++ .../camel/parser/model/CamelNodeDetails.java | 165 +++++++ .../parser/model/CamelNodeDetailsFactory.java | 37 ++ .../apache/camel/parser/model/CoverageData.java | 39 ++ .../parser/java/MyJavaDslRouteBuilder.java | 39 ++ .../camel/parser/java/RoasterJavaDslTest.java | 77 +++ .../java/RoasterJavaDslTwoRoutesTest.java | 86 ++++ .../parser/java/TwoRoutesRouteBuilder.java | 33 ++ .../camel/parser/xml/XmlParseTreeTest.java | 58 +++ .../org/apache/camel/parser/xml/mycamel.xml | 2 +- .../apache/camel/maven/RouteCoverageMojo.java | 408 ++++++++++++++++ .../camel/maven/model/RouteCoverageNode.java | 77 +++ 43 files changed, 2764 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/camel-core/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java b/camel-core/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java index 4633ece..a28ff71 100644 --- a/camel-core/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java +++ b/camel-core/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java @@ -96,7 +96,20 @@ public final class RouteCoverageXmlParser { el.setAttribute("totalProcessingTime", "" + totalTime); } } else if ("from".equals(qName)) { - // TODO: include the stats from the route mbean as that would be the same + // grab statistics from the parent route as from would be the same + Element parent = elementStack.peek(); + if (parent != null) { + String routeId = parent.getAttribute("id"); + ManagedRouteMBean route = camelContext.getManagedRoute(routeId, ManagedRouteMBean.class); + if (route != null) { + long total = route.getExchangesTotal(); + el.setAttribute("exchangesTotal", "" + total); + long totalTime = route.getTotalProcessingTime(); + el.setAttribute("totalProcessingTime", "" + totalTime); + // from is index-0 + el.setAttribute("index", "0"); + } + } } else { ManagedProcessorMBean processor = camelContext.getManagedProcessor(id, ManagedProcessorMBean.class); if (processor != null) { @@ -104,6 +117,8 @@ public final class RouteCoverageXmlParser { el.setAttribute("exchangesTotal", "" + total); long totalTime = processor.getTotalProcessingTime(); el.setAttribute("totalProcessingTime", "" + totalTime); + int index = processor.getIndex(); + el.setAttribute("index", "" + index); } } } catch (Exception e) { http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelAnnotationsHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelAnnotationsHandler.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelAnnotationsHandler.java index 8902f4f..071891f 100644 --- a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelAnnotationsHandler.java +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelAnnotationsHandler.java @@ -23,6 +23,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import org.apache.camel.component.properties.PropertiesComponent; import org.apache.camel.impl.DefaultDebugger; @@ -30,6 +31,7 @@ import org.apache.camel.impl.InterceptSendToMockEndpointStrategy; import org.apache.camel.management.JmxSystemPropertyKeys; import org.apache.camel.spi.Breakpoint; import org.apache.camel.spi.Debugger; +import org.apache.camel.spi.EventNotifier; import org.apache.camel.spring.SpringCamelContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +71,29 @@ public final class CamelAnnotationsHandler { } } + /** + * Handles disabling of JMX on Camel contexts based on {@link DisableJmx}. + * + * @param context the initialized Spring context + * @param testClass the test class being executed + */ + public static void handleRouteCoverage(ConfigurableApplicationContext context, Class<?> testClass, Function testMethod) throws Exception { + if (testClass.isAnnotationPresent(EnableRouteCoverage.class)) { + System.setProperty("CamelTestRouteCoverage", "true"); + + CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() { + + @Override + public void execute(String contextName, SpringCamelContext camelContext) throws Exception { + LOGGER.info("Enabling RouteCoverage"); + EventNotifier notifier = new RouteCoverageEventNotifier(testClass.getName(), testMethod); + camelContext.addService(notifier, true); + camelContext.getManagementStrategy().addEventNotifier(notifier); + } + }); + } + } + public static void handleProvidesBreakpoint(ConfigurableApplicationContext context, Class<?> testClass) throws Exception { Collection<Method> methods = getAllMethods(testClass); final List<Breakpoint> breakpoints = new LinkedList<Breakpoint>(); http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringBootExecutionListener.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringBootExecutionListener.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringBootExecutionListener.java index 546462d..195f57d 100644 --- a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringBootExecutionListener.java +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringBootExecutionListener.java @@ -30,7 +30,7 @@ public class CamelSpringBootExecutionListener extends AbstractTestExecutionListe @Override public void prepareTestInstance(TestContext testContext) throws Exception { - LOG.info("@RunWith(CamelSpringBootJUnit4ClassRunner.class) preparing: {}", testContext.getTestClass()); + LOG.info("@RunWith(CamelSpringBootRunner.class) preparing: {}", testContext.getTestClass()); Class<?> testClass = testContext.getTestClass(); // we are customizing the Camel context with @@ -56,15 +56,20 @@ public class CamelSpringBootExecutionListener extends AbstractTestExecutionListe @Override public void beforeTestMethod(TestContext testContext) throws Exception { - LOG.info("@RunWith(CamelSpringBootJUnit4ClassRunner.class) before: {}.{}", testContext.getTestClass(), testContext.getTestMethod().getName()); + LOG.info("@RunWith(CamelSpringBootRunner.class) before: {}.{}", testContext.getTestClass(), testContext.getTestMethod().getName()); Class<?> testClass = testContext.getTestClass(); + String testName = testContext.getTestMethod().getName(); + ConfigurableApplicationContext context = (ConfigurableApplicationContext) testContext.getApplicationContext(); // mark Camel to be startable again and start Camel System.clearProperty("skipStartingCamelContext"); - LOG.info("Initialized CamelSpringBootJUnit4ClassRunner now ready to start CamelContext"); + // route coverage need to know the test method + CamelAnnotationsHandler.handleRouteCoverage(context, testClass, (String) -> testName); + + LOG.info("Initialized CamelSpringBootRunner now ready to start CamelContext"); CamelAnnotationsHandler.handleCamelContextStartup(context, testClass); } http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java index 380fac0..aa76c15 100644 --- a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringDelegatingTestContextLoader.java @@ -16,6 +16,8 @@ */ package org.apache.camel.test.spring; +import java.lang.reflect.Method; + import org.apache.camel.management.JmxSystemPropertyKeys; import org.apache.camel.spring.SpringCamelContext; import org.slf4j.Logger; @@ -77,6 +79,7 @@ public class CamelSpringDelegatingTestContextLoader extends DelegatingSmartConte AnnotationConfigUtils.registerAnnotationConfigProcessors((BeanDefinitionRegistry) context); // Post CamelContext(s) instantiation but pre CamelContext(s) start setup + CamelAnnotationsHandler.handleRouteCoverage(context, testClass, (String) -> getTestMethod().getName()); CamelAnnotationsHandler.handleProvidesBreakpoint(context, testClass); CamelAnnotationsHandler.handleShutdownTimeout(context, testClass); CamelAnnotationsHandler.handleMockEndpoints(context, testClass); @@ -119,4 +122,14 @@ public class CamelSpringDelegatingTestContextLoader extends DelegatingSmartConte return CamelSpringTestHelper.getTestClass(); } + /** + * Returns the test method under test. + * + * @return the method that is being executed + * @see CamelSpringTestHelper + */ + protected Method getTestMethod() { + return CamelSpringTestHelper.getTestMethod(); + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoader.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoader.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoader.java index b595ce3..434f188 100644 --- a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoader.java +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoader.java @@ -32,6 +32,7 @@ import org.apache.camel.impl.InterceptSendToMockEndpointStrategy; import org.apache.camel.management.JmxSystemPropertyKeys; import org.apache.camel.spi.Breakpoint; import org.apache.camel.spi.Debugger; +import org.apache.camel.spi.EventNotifier; import org.apache.camel.spring.SpringCamelContext; import org.apache.camel.test.ExcludingPackageScanClassResolver; import org.apache.camel.test.spring.CamelSpringTestHelper.DoToSpringCamelContextsStrategy; @@ -107,7 +108,7 @@ public class CamelSpringTestContextLoader extends AbstractContextLoader { public ApplicationContext loadContext(String... locations) throws Exception { Class<?> testClass = getTestClass(); - + if (LOG.isDebugEnabled()) { LOG.debug("Loading ApplicationContext for locations [" + StringUtils.arrayToCommaDelimitedString(locations) + "]."); } @@ -153,6 +154,7 @@ public class CamelSpringTestContextLoader extends AbstractContextLoader { SpringCamelContext.setNoStart(false); // Post CamelContext(s) instantiation but pre CamelContext(s) start setup + handleRouteCoverage(context, testClass); handleProvidesBreakpoint(context, testClass); handleShutdownTimeout(context, testClass); handleMockEndpoints(context, testClass); @@ -204,7 +206,6 @@ public class CamelSpringTestContextLoader extends AbstractContextLoader { if (mergedConfig != null) { parentContext = mergedConfig.getParentApplicationContext(); - } if (testClass.isAnnotationPresent(ExcludeRoutes.class)) { @@ -258,7 +259,7 @@ public class CamelSpringTestContextLoader extends AbstractContextLoader { */ protected void handleDisableJmx(GenericApplicationContext context, Class<?> testClass) { CamelSpringTestHelper.setOriginalJmxDisabledValue(System.getProperty(JmxSystemPropertyKeys.DISABLED)); - + if (testClass.isAnnotationPresent(DisableJmx.class)) { if (testClass.getAnnotation(DisableJmx.class).value()) { LOG.info("Disabling Camel JMX globally as DisableJmx annotation was found and disableJmx is set to true."); @@ -267,12 +268,36 @@ public class CamelSpringTestContextLoader extends AbstractContextLoader { LOG.info("Enabling Camel JMX as DisableJmx annotation was found and disableJmx is set to false."); System.clearProperty(JmxSystemPropertyKeys.DISABLED); } - } else { + } else if (!testClass.isAnnotationPresent(EnableRouteCoverage.class)) { + // route coverage need JMX so do not disable it by default LOG.info("Disabling Camel JMX globally for tests by default. Use the DisableJMX annotation to override the default setting."); System.setProperty(JmxSystemPropertyKeys.DISABLED, "true"); } } - + + /** + * Handles disabling of JMX on Camel contexts based on {@link DisableJmx}. + * + * @param context the initialized Spring context + * @param testClass the test class being executed + */ + private void handleRouteCoverage(GenericApplicationContext context, Class<?> testClass) throws Exception { + if (testClass.isAnnotationPresent(EnableRouteCoverage.class)) { + System.setProperty("CamelTestRouteCoverage", "true"); + + CamelSpringTestHelper.doToSpringCamelContexts(context, new DoToSpringCamelContextsStrategy() { + + @Override + public void execute(String contextName, SpringCamelContext camelContext) throws Exception { + LOG.info("Enabling RouteCoverage"); + EventNotifier notifier = new RouteCoverageEventNotifier(testClass.getName(), (String) -> getTestMethod().getName()); + camelContext.addService(notifier, true); + camelContext.getManagementStrategy().addEventNotifier(notifier); + } + }); + } + } + /** * Handles the processing of the {@link ProvidesBreakpoint} annotation on a test class. Exists here * as it is needed in @@ -504,4 +529,14 @@ public class CamelSpringTestContextLoader extends AbstractContextLoader { protected Class<?> getTestClass() { return CamelSpringTestHelper.getTestClass(); } + + /** + * Returns the test method under test. + * + * @return the method that is being executed + * @see CamelSpringTestHelper + */ + protected Method getTestMethod() { + return CamelSpringTestHelper.getTestMethod(); + } } http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoaderTestExecutionListener.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoaderTestExecutionListener.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoaderTestExecutionListener.java index d8533d1..100b998 100644 --- a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoaderTestExecutionListener.java +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestContextLoaderTestExecutionListener.java @@ -45,5 +45,6 @@ public class CamelSpringTestContextLoaderTestExecutionListener extends AbstractT @Override public void prepareTestInstance(TestContext testContext) throws Exception { CamelSpringTestHelper.setTestClass(testContext.getTestClass()); - } + CamelSpringTestHelper.setTestContext(testContext); + } } http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestHelper.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestHelper.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestHelper.java index aa75c0e..1c426b4 100644 --- a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestHelper.java +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestHelper.java @@ -27,6 +27,7 @@ import java.util.Set; import org.apache.camel.spring.SpringCamelContext; import org.springframework.context.ApplicationContext; +import org.springframework.test.context.TestContext; /** * Helper that provides state information across the levels of Spring Test that do not expose the @@ -41,7 +42,8 @@ public final class CamelSpringTestHelper { private static ThreadLocal<String> originalJmxDisabledValue = new ThreadLocal<String>(); private static ThreadLocal<Class<?>> testClazz = new ThreadLocal<Class<?>>(); - + private static ThreadLocal<TestContext> testContext = new ThreadLocal<TestContext>(); + private CamelSpringTestHelper() { } @@ -60,7 +62,15 @@ public final class CamelSpringTestHelper { public static void setTestClass(Class<?> testClass) { testClazz.set(testClass); } - + + public static Method getTestMethod() { + return testContext.get().getTestMethod(); + } + + public static void setTestContext(TestContext context) { + testContext.set(context); + } + /** * Returns all methods defined in {@code clazz} and its superclasses/interfaces. */ http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/EnableRouteCoverage.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/EnableRouteCoverage.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/EnableRouteCoverage.java new file mode 100644 index 0000000..13c8514 --- /dev/null +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/EnableRouteCoverage.java @@ -0,0 +1,39 @@ +/** + * 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.test.spring; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Enables dumping route coverage statistic. + * The route coverage status is written as xml files in the <tt>target/camel-route-coverage</tt> directory after the test has finished. + * <p/> + * This allows tooling or manual inspection of the stats, so you can generate a route trace diagram of which EIPs + * have been in use and which have not. Similar concepts as a code coverage report. + */ +@Documented +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface EnableRouteCoverage { + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/RouteCoverageEventNotifier.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/RouteCoverageEventNotifier.java b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/RouteCoverageEventNotifier.java new file mode 100644 index 0000000..17b7064 --- /dev/null +++ b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/RouteCoverageEventNotifier.java @@ -0,0 +1,98 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.test.spring; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.EventObject; +import java.util.function.Function; + +import org.apache.camel.CamelContext; +import org.apache.camel.api.management.mbean.ManagedCamelContextMBean; +import org.apache.camel.management.event.CamelContextStoppingEvent; +import org.apache.camel.support.EventNotifierSupport; +import org.apache.camel.util.IOHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RouteCoverageEventNotifier extends EventNotifierSupport { + + private static final Logger LOG = LoggerFactory.getLogger(RouteCoverageEventNotifier.class); + + private final String testClassName; + private final Function testMethodName; + + public RouteCoverageEventNotifier(String testClassName, Function testMethodName) { + this.testClassName = testClassName; + this.testMethodName = testMethodName; + setIgnoreCamelContextEvents(false); + setIgnoreExchangeEvents(true); + } + + @Override + public boolean isEnabled(EventObject event) { + return event instanceof CamelContextStoppingEvent; + } + + @Override + public void notify(EventObject event) throws Exception { + CamelContext context = ((CamelContextStoppingEvent) event).getContext(); + try { + String className = this.getClass().getSimpleName(); + String dir = "target/camel-route-coverage"; + String testName = (String) testMethodName.apply(this); + String name = className + "-" + testName + ".xml"; + + ManagedCamelContextMBean managedCamelContext = context.getManagedCamelContext(); + if (managedCamelContext == null) { + LOG.warn("Cannot dump route coverage to file as JMX is not enabled. Override useJmx() method to enable JMX in the unit test classes."); + } else { + String xml = managedCamelContext.dumpRoutesCoverageAsXml(); + String combined = "<camelRouteCoverage>\n" + gatherTestDetailsAsXml(testName) + xml + "\n</camelRouteCoverage>"; + + File file = new File(dir); + // ensure dir exists + file.mkdirs(); + file = new File(dir, name); + + LOG.info("Dumping route coverage to file: " + file); + InputStream is = new ByteArrayInputStream(combined.getBytes()); + OutputStream os = new FileOutputStream(file, false); + IOHelper.copyAndCloseInput(is, os); + IOHelper.close(os); + } + } catch (Exception e) { + LOG.warn("Error during dumping route coverage statistic. This exception is ignored.", e); + } + } + + /** + * Gathers test details as xml + */ + private String gatherTestDetailsAsXml(String testName) { + StringBuilder sb = new StringBuilder(); + sb.append("<test>\n"); + sb.append(" <class>").append(testClassName).append("</class>\n"); + sb.append(" <method>").append(testName).append("</method>\n"); + sb.append("</test>\n"); + return sb.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRouteProcessorDumpRouteCoverageTest.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRouteProcessorDumpRouteCoverageTest.java b/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRouteProcessorDumpRouteCoverageTest.java new file mode 100644 index 0000000..9374d2f --- /dev/null +++ b/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRouteProcessorDumpRouteCoverageTest.java @@ -0,0 +1,55 @@ +/** + * 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.test.spring; + +import java.io.File; + +import org.apache.camel.management.ManagedManagementStrategy; +import org.apache.camel.test.junit4.TestSupport; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@EnableRouteCoverage +public class CamelSpringRouteProcessorDumpRouteCoverageTest extends CamelSpringRunnerPlainTest { + + @BeforeClass + public static void prepareFiles() throws Exception { + TestSupport.deleteDirectory("target/camel-route-coverage"); + } + + @Test + public void testJmx() throws Exception { + // JMX is enabled with route coverage + assertEquals(ManagedManagementStrategy.class, camelContext.getManagementStrategy().getClass()); + } + + @Override + public void testRouteCoverage() throws Exception{ + camelContext.stop(); + camelContext2.stop(); + + // there should be files + String[] names = new File("target/camel-route-coverage").list(); + assertNotNull(names); + assertTrue(names.length > 0); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRunnerPlainTest.java ---------------------------------------------------------------------- diff --git a/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRunnerPlainTest.java b/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRunnerPlainTest.java index 17fa2d6..39b2d88 100644 --- a/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRunnerPlainTest.java +++ b/components/camel-test-spring/src/test/java/org/apache/camel/test/spring/CamelSpringRunnerPlainTest.java @@ -118,6 +118,11 @@ public class CamelSpringRunnerPlainTest { assertNull(camelContext2.getDebugger()); } + @Test + public void testRouteCoverage() throws Exception { + // noop + } + } // end::example[] // END SNIPPET: e1 http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java ---------------------------------------------------------------------- diff --git a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java index 632b8b0..2206822 100644 --- a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java +++ b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java @@ -391,7 +391,8 @@ public abstract class CamelTestSupport extends TestSupport { log.info("Took: " + TimeUtils.printDuration(time) + " (" + time + " millis)"); // if we should dump route stats, then write that to a file - if (isDumpRouteCoverage()) { + boolean coverage = System.getProperty("CamelTestRouteCoverage", "false").equalsIgnoreCase("true") || isDumpRouteCoverage(); + if (coverage) { String className = this.getClass().getSimpleName(); String dir = "target/camel-route-coverage"; String name = className + "-" + getTestMethodName() + ".xml"; @@ -485,7 +486,6 @@ public abstract class CamelTestSupport extends TestSupport { builder.append(routesSummary); log.info(builder.toString()); - } /** http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/pom.xml ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/pom.xml b/examples/camel-example-spring-boot-xml/pom.xml new file mode 100644 index 0000000..3501809 --- /dev/null +++ b/examples/camel-example-spring-boot-xml/pom.xml @@ -0,0 +1,151 @@ +<?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.example</groupId> + <artifactId>examples</artifactId> + <version>2.20.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-example-spring-boot-xml</artifactId> + <name>Camel :: Example :: Spring Boot XML</name> + <description>An example showing how to work with Camel routes in XML files and Spring Boot</description> + + <properties> + <category>Beginner</category> + + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <spring.boot-version>${spring-boot-version}</spring.boot-version> + </properties> + + <dependencyManagement> + <dependencies> + <!-- Spring Boot BOM --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring.boot-version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <!-- Camel BOM --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring-boot-dependencies</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + + <!-- Spring Boot --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-undertow</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-actuator</artifactId> + </dependency> + + <!-- Camel --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-stream-starter</artifactId> + </dependency> + + <!-- test --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-spring</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>${spring-boot-version}</version> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.camel</groupId> + <artifactId>camel-maven-plugin</artifactId> + <version>${project.version}</version> + <!-- allows to fail if not all routes are fully covered during testing --> +<!-- + <configuration> + <failOnError>true</failOnError> + </configuration> +--> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>jdk9-build</id> + <activation> + <jdk>9</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>--add-modules java.xml.bind --add-opens java.base/java.lang=ALL-UNNAMED</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/readme.adoc ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/readme.adoc b/examples/camel-example-spring-boot-xml/readme.adoc new file mode 100644 index 0000000..72475b2 --- /dev/null +++ b/examples/camel-example-spring-boot-xml/readme.adoc @@ -0,0 +1,41 @@ +# Camel Example Spring Boot + +This example shows how to work with a simple Apache Camel application using Spring Boot. + +The example generates messages using timer trigger, writes them to standard output. + +## Camel routes + +The Camel route is located in the `SampleCamelRouter` class. In this class the route +starts from a timer, that triggers every 2nd second and calls a Spring Bean `SampleBean` +which returns a message, that is routed to a stream endpoint which writes to standard output. + +## Using Camel components + +Apache Camel provides 200+ components which you can use to integrate and route messages between many systems +and data formats. To use any of these Camel components, add the component as a dependency to your project. + +## How to run + +You can run this example using + + mvn spring-boot:run + +## To get info about the routes + +To show a summary of all the routes + +---- +curl -XGET -s http://localhost:8080/camel/routes +---- + +To show detailed information for a specific route + +---- +curl -XGET -s http://localhost:8080/camel/routes/{id}/info +---- + + +## More information + +You can find more information about Apache Camel at the website: http://camel.apache.org/ http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleBean.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleBean.java b/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleBean.java new file mode 100644 index 0000000..b60ef69 --- /dev/null +++ b/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleBean.java @@ -0,0 +1,38 @@ +/** + * 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 sample.camel; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * A bean that returns a message when you call the {@link #saySomething()} method. + * <p/> + * Uses <tt>@Component("myBean")</tt> to register this bean with the name <tt>myBean</tt> + * that we use in the Camel route to lookup this bean. + */ +@Component("myBean") +public class SampleBean { + + @Value("${greeting}") + private String say; + + public String saySomething() { + return say; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleCamelApplication.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleCamelApplication.java b/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleCamelApplication.java new file mode 100644 index 0000000..a678dae --- /dev/null +++ b/examples/camel-example-spring-boot-xml/src/main/java/sample/camel/SampleCamelApplication.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 sample.camel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ImportResource; + +//CHECKSTYLE:OFF +/** + * A sample Spring Boot application that starts the Camel routes. + */ +@SpringBootApplication +// load the spring xml file from classpath +@ImportResource("classpath:my-camel.xml") +public class SampleCamelApplication { + + /** + * A main method to start this application. + */ + public static void main(String[] args) { + SpringApplication.run(SampleCamelApplication.class, args); + } + +} +//CHECKSTYLE:ON http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/src/main/resources/application.properties ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/src/main/resources/application.properties b/examples/camel-example-spring-boot-xml/src/main/resources/application.properties new file mode 100644 index 0000000..75a10de --- /dev/null +++ b/examples/camel-example-spring-boot-xml/src/main/resources/application.properties @@ -0,0 +1,46 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +# the name of Camel +camel.springboot.name = SampleCamel + +# to automatic shutdown the JVM after a period of time +#camel.springboot.duration-max-seconds=60 +#camel.springboot.duration-max-messages=100 + +# add for example: &repeatCount=5 to the timer endpoint to make Camel idle +#camel.springboot.duration-max-idle-seconds=15 + +# properties used in the Camel route and beans +# -------------------------------------------- + +# what to say +greeting = Hello World + +# how often to trigger the timer +timer.period = 2000 + +# all access to actuator endpoints without security +management.security.enabled = false +# turn on actuator health check +endpoints.health.enabled = true + +# to configure logging levels +#logging.level.org.springframework = INFO +#logging.level.org.apache.camel.spring.boot = INFO +#logging.level.org.apache.camel.impl = DEBUG +#logging.level.sample.camel = DEBUG http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/src/main/resources/my-camel.xml ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/src/main/resources/my-camel.xml b/examples/camel-example-spring-boot-xml/src/main/resources/my-camel.xml new file mode 100644 index 0000000..3fc6e0f --- /dev/null +++ b/examples/camel-example-spring-boot-xml/src/main/resources/my-camel.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> + + <camelContext id="SampleCamel" xmlns="http://camel.apache.org/schema/spring"> + <route id="hello"> + <from uri="timer:hello?period={{timer.period}}"/> + <transform> + <method ref="myBean" method="saySomething"/> + </transform> + <filter> + <simple>${body} contains 'foo'</simple> + <to uri="log:foo"/> + </filter> + <to uri="stream:out"/> + </route> + </camelContext> + +</beans> http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/FooApplicationTest.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/FooApplicationTest.java b/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/FooApplicationTest.java new file mode 100644 index 0000000..5a71fc9 --- /dev/null +++ b/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/FooApplicationTest.java @@ -0,0 +1,52 @@ +/** + * 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 sample.camel; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.NotifyBuilder; +import org.apache.camel.test.spring.CamelSpringBootRunner; +import org.apache.camel.test.spring.EnableRouteCoverage; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.Assert.assertTrue; + +@RunWith(CamelSpringBootRunner.class) +@SpringBootTest(classes = SampleCamelApplication.class, + properties = "greeting = Hello foo") +@EnableRouteCoverage +@Ignore // enable me to run this test as well so we can cover testing the route completely +public class FooApplicationTest { + + @Autowired + private CamelContext camelContext; + + @Test + public void shouldSayFoo() throws Exception { + // we expect that one or more messages is automatic done by the Camel + // route as it uses a timer to trigger + NotifyBuilder notify = new NotifyBuilder(camelContext).whenDone(1).create(); + + assertTrue(notify.matches(10, TimeUnit.SECONDS)); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/SampleCamelApplicationTest.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/SampleCamelApplicationTest.java b/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/SampleCamelApplicationTest.java new file mode 100644 index 0000000..f4c2fc5 --- /dev/null +++ b/examples/camel-example-spring-boot-xml/src/test/java/sample/camel/SampleCamelApplicationTest.java @@ -0,0 +1,49 @@ +/** + * 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 sample.camel; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.NotifyBuilder; +import org.apache.camel.test.spring.CamelSpringBootRunner; +import org.apache.camel.test.spring.EnableRouteCoverage; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.Assert.assertTrue; + +@RunWith(CamelSpringBootRunner.class) +@SpringBootTest(classes = SampleCamelApplication.class) +@EnableRouteCoverage +public class SampleCamelApplicationTest { + + @Autowired + private CamelContext camelContext; + + @Test + public void shouldProduceMessages() throws Exception { + // we expect that one or more messages is automatic done by the Camel + // route as it uses a timer to trigger + NotifyBuilder notify = new NotifyBuilder(camelContext).whenDone(1).create(); + + assertTrue(notify.matches(10, TimeUnit.SECONDS)); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot/pom.xml ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot/pom.xml b/examples/camel-example-spring-boot/pom.xml index dbf3333..3467c2a 100644 --- a/examples/camel-example-spring-boot/pom.xml +++ b/examples/camel-example-spring-boot/pom.xml @@ -115,6 +115,18 @@ </execution> </executions> </plugin> + + <plugin> + <groupId>org.apache.camel</groupId> + <artifactId>camel-maven-plugin</artifactId> + <version>${project.version}</version> + <!-- allows to fail if not all routes are fully covered during testing --> +<!-- + <configuration> + <failOnError>true</failOnError> + </configuration> +--> + </plugin> </plugins> </build> http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java b/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java index 4e0422e..4b11938 100644 --- a/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java +++ b/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java @@ -29,8 +29,11 @@ public class SampleCamelRouter extends RouteBuilder { @Override public void configure() throws Exception { - from("timer:hello?period={{timer.period}}") - .transform(method("myBean", "saySomething")) + from("timer:hello?period={{timer.period}}").routeId("hello") + .transform().method("myBean", "saySomething") + .filter(simple("${body} contains 'foo'")) + .to("log:foo") + .end() .to("stream:out"); } http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java b/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java new file mode 100644 index 0000000..45f2076 --- /dev/null +++ b/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java @@ -0,0 +1,52 @@ +/** + * 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 sample.camel; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.NotifyBuilder; +import org.apache.camel.test.spring.CamelSpringBootRunner; +import org.apache.camel.test.spring.EnableRouteCoverage; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.Assert.assertTrue; + +@RunWith(CamelSpringBootRunner.class) +@SpringBootTest(classes = SampleCamelApplication.class, + properties = "greeting = Hello foo") +@EnableRouteCoverage +//@Ignore // enable me to run this test as well so we can cover testing the route completely +public class FooApplicationTest { + + @Autowired + private CamelContext camelContext; + + @Test + public void shouldSayFoo() throws Exception { + // we expect that one or more messages is automatic done by the Camel + // route as it uses a timer to trigger + NotifyBuilder notify = new NotifyBuilder(camelContext).whenDone(1).create(); + + assertTrue(notify.matches(10, TimeUnit.SECONDS)); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/camel-example-spring-boot/src/test/java/sample/camel/SampleCamelApplicationTest.java ---------------------------------------------------------------------- diff --git a/examples/camel-example-spring-boot/src/test/java/sample/camel/SampleCamelApplicationTest.java b/examples/camel-example-spring-boot/src/test/java/sample/camel/SampleCamelApplicationTest.java index 7d53276..f4c2fc5 100644 --- a/examples/camel-example-spring-boot/src/test/java/sample/camel/SampleCamelApplicationTest.java +++ b/examples/camel-example-spring-boot/src/test/java/sample/camel/SampleCamelApplicationTest.java @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import org.apache.camel.CamelContext; import org.apache.camel.builder.NotifyBuilder; import org.apache.camel.test.spring.CamelSpringBootRunner; +import org.apache.camel.test.spring.EnableRouteCoverage; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +31,7 @@ import static org.junit.Assert.assertTrue; @RunWith(CamelSpringBootRunner.class) @SpringBootTest(classes = SampleCamelApplication.class) +@EnableRouteCoverage public class SampleCamelApplicationTest { @Autowired http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/examples/pom.xml ---------------------------------------------------------------------- diff --git a/examples/pom.xml b/examples/pom.xml index f8cdc10..717f593 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -108,6 +108,7 @@ <module>camel-example-spring-boot-rest-swagger</module> <module>camel-example-spring-boot-servicecall</module> <module>camel-example-spring-boot-supervising-route-controller</module> + <module>camel-example-spring-boot-xml</module> <module>camel-example-spring-cloud-servicecall</module> <module>camel-example-spring-javaconfig</module> <module>camel-example-spring-jms</module> http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/tooling/camel-route-parser/pom.xml ---------------------------------------------------------------------- diff --git a/tooling/camel-route-parser/pom.xml b/tooling/camel-route-parser/pom.xml index 0f65e3e..4cfc2bd 100644 --- a/tooling/camel-route-parser/pom.xml +++ b/tooling/camel-route-parser/pom.xml @@ -42,6 +42,13 @@ <scope>provided</scope> </dependency> + <!-- the catalog has details the parser needs to parse the Camel Java DSL --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-catalog</artifactId> + <version>${project.version}</version> + </dependency> + <!-- only test scopes for camel as we have no runtime dependency on camel --> <dependency> <groupId>org.apache.camel</groupId> http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java ---------------------------------------------------------------------- diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java index d38e1d6..96e54b2 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java @@ -20,10 +20,13 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import org.apache.camel.parser.helper.CamelJavaTreeParserHelper; import org.apache.camel.parser.helper.CamelJavaParserHelper; import org.apache.camel.parser.model.CamelEndpointDetails; +import org.apache.camel.parser.model.CamelNodeDetails; import org.apache.camel.parser.model.CamelRouteDetails; import org.apache.camel.parser.model.CamelSimpleExpressionDetails; import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode; @@ -48,6 +51,42 @@ public final class RouteBuilderParser { } /** + * Parses the java source class and build a route model (tree) of the discovered routes in the java source class. + * + * @param clazz the java source class + * @param baseDir the base of the source code + * @param fullyQualifiedFileName the fully qualified source code file name + * @return a list of route model (tree) of each discovered route + */ + public static List<CamelNodeDetails> parseRouteBuilderTree(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName, + boolean includeInlinedRouteBuilders) { + + List<MethodSource<JavaClassSource>> methods = new ArrayList<>(); + MethodSource<JavaClassSource> method = CamelJavaParserHelper.findConfigureMethod(clazz); + if (method != null) { + methods.add(method); + } + if (includeInlinedRouteBuilders) { + List<MethodSource<JavaClassSource>> inlinedMethods = CamelJavaParserHelper.findInlinedConfigureMethods(clazz); + if (!inlinedMethods.isEmpty()) { + methods.addAll(inlinedMethods); + } + } + + CamelJavaTreeParserHelper parser = new CamelJavaTreeParserHelper(); + List<CamelNodeDetails> list = new ArrayList<>(); + for (MethodSource<JavaClassSource> configureMethod : methods) { + // there may be multiple route builder configure methods + List<CamelNodeDetails> details = parser.parseCamelRouteTree(clazz, baseDir, fullyQualifiedFileName, configureMethod); + list.addAll(details); + } + // we end up parsing bottom->up so reverse list + Collections.reverse(list); + + return list; + } + + /** * Parses the java source class to discover Camel endpoints. * * @param clazz the java source class http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java ---------------------------------------------------------------------- diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java index 364cf9a..5512837 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java @@ -17,8 +17,12 @@ package org.apache.camel.parser; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; +import org.apache.camel.parser.helper.CamelXmlTreeParserHelper; +import org.apache.camel.parser.model.CamelNodeDetails; +import org.apache.camel.parser.model.CamelNodeDetailsFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -44,6 +48,59 @@ public final class XmlRouteParser { } /** + * Parses the XML file and build a route model (tree) of the discovered routes in the XML source file. + * + * @param xml the xml file as input stream + * @param baseDir the base of the source code + * @param fullyQualifiedFileName the fully qualified source code file name + * @return a list of route model (tree) of each discovered route + */ + public static List<CamelNodeDetails> parseXmlRouteTree(InputStream xml, String baseDir, String fullyQualifiedFileName) { + List<CamelNodeDetails> answer = new ArrayList<>(); + + // try parse it as dom + Document dom = null; + try { + dom = XmlLineNumberParser.parseXml(xml); + } catch (Exception e) { + // ignore as the xml file may not be valid at this point + } + if (dom != null) { + + // find any from which is the start of the route + CamelNodeDetailsFactory nodeFactory = CamelNodeDetailsFactory.newInstance(); + + CamelXmlTreeParserHelper parser = new CamelXmlTreeParserHelper(); + + List<Node> routes = CamelXmlHelper.findAllRoutes(dom); + for (Node route : routes) { + // parse each route and build + String routeId = getSafeAttribute(route, "id"); + String lineNumber = (String) route.getUserData(XmlLineNumberParser.LINE_NUMBER); + String lineNumberEnd = (String) route.getUserData(XmlLineNumberParser.LINE_NUMBER_END); + + // we only want the relative dir name from the resource directory, eg META-INF/spring/foo.xml + String fileName = fullyQualifiedFileName; + if (fileName.startsWith(baseDir)) { + fileName = fileName.substring(baseDir.length() + 1); + } + + CamelNodeDetails node = nodeFactory.newNode(null, "route"); + node.setRouteId(routeId); + node.setFileName(fileName); + node.setLineNumber(lineNumber); + node.setLineNumberEnd(lineNumberEnd); + + // parse the route and gather all its EIPs + List<CamelNodeDetails> tree = parser.parseCamelRouteTree(route, routeId, node, baseDir, fullyQualifiedFileName); + answer.addAll(tree); + } + } + + return answer; + } + + /** * Parses the XML source to discover Camel endpoints. * * @param xml the xml file as input stream http://git-wip-us.apache.org/repos/asf/camel/blob/f015f7b0/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/graph/RenderRoute.java ---------------------------------------------------------------------- diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/graph/RenderRoute.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/graph/RenderRoute.java new file mode 100644 index 0000000..a1f4807 --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/graph/RenderRoute.java @@ -0,0 +1,70 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.parser.graph; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +import org.apache.camel.parser.model.CamelNodeDetails; + +/** + * @deprecated experiment to render a route via Java image + */ +@Deprecated +public class RenderRoute { + + public static void main(String[] args) { + RenderRoute render = new RenderRoute(); + render(null); + } + + public static void render(CamelNodeDetails root) { + // TODO: + try { + int width = 200, height = 200; + + // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed + // into integer pixels +// BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + + Graphics2D ig2 = bi.createGraphics(); + + ig2.drawRect(10, 10, 80, 40); + ig2.drawLine(45, 50, 45, 80); + ig2.drawRect(10, 80, 80, 40); + + Font font = new Font("Arial", Font.BOLD, 20); + ig2.setFont(font); + String message = "Apache Camel"; + FontMetrics fontMetrics = ig2.getFontMetrics(); + int stringWidth = fontMetrics.stringWidth(message); + int stringHeight = fontMetrics.getAscent(); + ig2.setPaint(Color.black); + ig2.drawString(message, (width - stringWidth) / 2, height / 2 + stringHeight / 4); + + ImageIO.write(bi, "PNG", new File("target/route.png")); + + } catch (IOException ie) { + ie.printStackTrace(); + } + + } +}