我有一个非常简单的Camel路由定义,它只包含一些OnException谓词来处理各自的异常和一些日志语句。
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参数并抛出一个适当的异常
public class TestBean
{
@Handler
public void checkData(@Headers final Map<String, Object> 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<RouteBuilder> routes()
{
final List<RouteBuilder> 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<String, Object> headers = new HashMap<>();
headers.put("TestValue", new Integer(1));
template.sendBodyAndHeaders("hazelcast:seda:someQueue", new Object(), headers);
...
ioErrorEndpoint.assertIsSatisfied();
...
}
}
此处测试只是替换IOException的onException段,首先将重新传递延迟从3分钟减少到10毫秒,并在结尾处添加一个模拟端点。但是,当我尝试运行单元测试时,我将得到以下异常:
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.
然而,就我所理解的而言,official documentation中的例子非常相似。我还尝试通过定义的ID谓词及其相应的方法weaveById()
或通过weaveByToString()
方法查找异常定义,但没有其他结果。我还尝试通过weaveByType(OnExceptionDefinition.class).selectIndex(1).remove();
删除异常定义,并通过weaveAddFirst().onException(...).async...;
添加OnException部分,但结果相同。
然而,可以通过f.e.附加模拟的错误端点。 weaveByToString("Log[io exception noticed]").after().to("mock:ioError");
因此,非常欢迎任何修改onException块或redeliveryDelay以进行单元测试的提示。
@Edit:我现在也尝试按照异常消息的建议将onException声明移到路由定义(from(...)
)之上,这也是Camel exception samples中的首选案例。但是,在执行此操作时,所有测试(甚至是工作测试)都会因NullPointerException
上的context.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... });
而失败,因为显然无法找到路径本身。我怀疑这是一个IntelliJ问题,因为这两个类都在同一个项目中,因此对于测试类应该可以看到路径的修改。
正在使用的Camel版本:2.13.0,IntelliJ IDEA 13.1.2
@ Edit2:由于某种原因,context.getRouteDefinitions("someQueueID")
如果OnException元素在from
块之外定义,则返回null,而一般路由可以通过context.getRouteDefinitions().get(0)
获得 - 但是,异常声明OnException部分需要作为顶级元素添加。
答案 0 :(得分:6)
使用Java DSL时,路由的ID是使用.routeId()
方法设置的,而不是上面编码的.id()
。这可能有助于解决您的adviceWith
问题。
更好的方法是使用属性来配置延迟,而不是对重试延迟进行硬编码。查看useOverridePropertiesWithPropertiesComponent()
课程中CamelSpringTestSupport
方法的文档。
修改强>
您不必编织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;
}
}