使用带有OnException定义的adviceWith进行Camel路由测试

我有一个非常简单的Camel路由定义,它只包含一些OnException谓词来处理各自的exception和一些日志语句。

from("hazelcast:seda:someQueue") .id("someQueueID") .onException(CustomException.class) .handled(true) .log(LoggingLevel.WARN, "custom exception noticed") .end() .onException(IOException.class, FileNotFoundException.class) .asyncDelayedRedelivery() .redeliveryDelay(3*1000*60) // 3 Minutes .maximumRedeliveries(3) .log(LoggingLevel.WARN, "io exception noticed") .end() .onException(Exception.class) .log(LoggingLevel.WARN, "general exception noticed") .end() .log("Starting route") .bean(TestBean.class) .log("Finished route"); 

bean本身也很简单,只是检查一个header参数并抛出一个适当的exception

 public class TestBean { @Handler public void checkData(@Headers final Map headers) throws CustomException, IOException, Exception { Integer testVal = (Integer)headers.get("TestValue"); if (0 == testVal) throw new CustomException("CustomException"); else if (1 == testVal) throw new IOException("IOException"); else throw new Exception("Exception"); } } 

由于这个测试设置只是一个较大项目的一小部分,这样做可能听起来很愚蠢,但核心意图是在测试时修改redeliveryDelay,因为“强制”IOException不需要等待3分钟因此,为了加快单位测试,可以将重新传递延迟减少到10毫秒。

为了实现这一点,我的测试方法执行以下操作:

 @ContextConfiguration(classes = OnExceptionRouteTest.ContextConfig.class, loader = AnnotationConfigContextLoader.class) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class OnExceptionRouteTest extends CamelSpringTestSupport { @Override protected AbstractApplicationContext createApplicationContext() { return new AnnotationConfigApplicationContext(ContextConfig.class) } @Configuration public static class ContextConfig extends CamelConfiguration { @Override protected void setupCamelContext(CamelContext camelContext) throws Exception { super.setupCamelContext(camelContext); camelContext.addComponent("hazelcast", new StubComponent()); // some other unnecessary stuff } @Override public List routes() { final List list = new ArrayList(); list.add(new OnExceptionRoute()); return list; } } @Override public boolean isUseAdviceWith() { return true; } @Test public void testIOException() { context.getRouteDefinition("someQueueID") .adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { this.weaveByType(OnExceptionDefinition.class) .selectIndex(1) .replace() .onException(IOException.class, FileNotFound.class) .asyncDelayedRedelivery() .redeliveryDelay(10) .maximumRedeliveries(3) .log("modified io exception noticed") .to("mock:ioError") .end(); ... mockEndpoints(); } }); context.start(); MockEndpoint ioErrorEndpoint = getMockEndpoint("mock:ioError"); ... ioErrorEndpoint.setExpectedMessageCount(1); ... Map headers = new HashMap(); headers.put("TestValue", new Integer(1)); template.sendBodyAndHeaders("hazelcast:seda:someQueue", new Object(), headers); ... ioErrorEndpoint.assertIsSatisfied(); ... } } 

这里测试只是替换IOException的onException段,首先将重新传递延迟从3分钟减少到10毫秒,并在结尾处添加一个模拟端点。 但是,当我尝试运行unit testing时,我将得到以下exception:

 java.lang.IllegalArgumentException: The output must be added as top-level on the route. Try moving OnException[[class java.io.IOException, class java.io.FileNotFoundException] -> []] to the top of route. 

但是,正如我所理解的那样, 官方文档中的示例非常相似。 我还尝试通过定义的ID谓词及其对应的方法weaveById()或通过weaveByToString()方法查找exception定义,但没有其他结果。 我还尝试通过weaveByType(OnExceptionDefinition.class).selectIndex(1).remove();删除exception定义weaveByType(OnExceptionDefinition.class).selectIndex(1).remove(); 并通过weaveAddFirst().onException(...).async...;添加OnException部分weaveAddFirst().onException(...).async...; 但结果相同。

但是,可以通过fe weaveByToString("Log[io exception noticed]").after().to("mock:ioError");追加weaveByToString("Log[io exception noticed]").after().to("mock:ioError");错误端点weaveByToString("Log[io exception noticed]").after().to("mock:ioError");

所以任何修改onException块的提示或者用于unit testing的redeliveryDelay都是非常受欢迎的。


@Edit:我现在也尝试按照exception消息的建议将onException声明移到路由定义之上( from(...) ),这也是Camel exception样本中的首选情况。 但是,在执行此操作时,所有测试(甚至是工作的)都会因context.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... });上的NullPointerException失败context.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... }); 很明显,这条路线本身已经找不到了。 我怀疑这是一个IntelliJ问题,因为这两个类都在同一个项目中,因此对于测试类应该可以看到路径的修改。

使用的Camel版本:2.13.0,IntelliJ IDEA 13.1.2


@ Edit2:出于某种原因,如果OnException元素在from块之外定义,则context.getRouteDefinitions("someQueueID")返回null,而一般路由可以通过context.getRouteDefinitions().get(0) – 但是,exception声明OnException部分需要添加为顶级元素保留。

使用Java DSL时,路由的id是使用.routeId()方法设置的,而不是上面编码的.id() 。 这可能对您的adviceWith有所帮助。

而不是硬编码重试延迟,更好的方法是使用属性使延迟可配置。 查看CamelSpringTestSupport类中方法useOverridePropertiesWithPropertiesComponent()的文档。

编辑

您不必编织onException子句,只需声明一个新子句。 这是一个完整的例子:

 import org.apache.camel.builder.AdviceWithRouteBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.test.junit4.CamelTestSupport; public class DummyTest extends CamelTestSupport{ @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder(){ @Override public void configure() throws Exception { from("direct://start") .routeId("myroute") .onException(Exception.class) .id("myException") .continued(true) .end() .throwException(new Exception()) .to("mock:end"); } }; } @org.junit.Test public void doTest() throws Exception{ context.getRouteDefinition("myroute").adviceWith(context, new AdviceWithRouteBuilder(){ @Override public void configure() throws Exception { context.getRouteDefinition("myroute") .onException(Exception.class).setBody(constant("adviceWith")).continued(true); }}); context.start(); template.sendBody("direct://start", "original"); String bodyAtEndOfExchange = getMockEndpoint("mock:end") .getExchanges().get(0).getIn().getBody(String.class); assertEquals("adviceWith", bodyAtEndOfExchange); context.stop(); } @Override public boolean isUseAdviceWith() { return true; } }