Camel route-testing using adviceWith with OnException definitions

拈花ヽ惹草 提交于 2019-12-09 11:34:53

问题


I have a very simple Camel route definition which just includes some OnException predicates to handle respective exceptions and some log-statements.

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");

The bean itself is simple too, it just checks a header parameter and throws an appropriate exception

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");
    }
}

As this test-setup is just a small part of a larger project it may sound silly to do it like presented here, but the core intend is to modify the redeliveryDelay at test time as a "forced" IOException will not need to wait 3 minutes and therefore, to speed up unit tests a bit, the redelivery delay could be reduced to like 10 ms.

In order to achieve this my test-method does the following:

@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();
        ...
    }
}

Here the test just replaces the onException segment of the IOException to first reduce the redelivery delay from 3 minutes to 10 ms and adds a mock endpoint at the end. However when I try to run the unit test I'll get the following 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.

However, the examples in the official documentation, as far as I understood them correctly, are very similar. I also tried to lookup the exception definition via a defined ID predicate and its corresponding method weaveById() or via the weaveByToString() method but with no other result. I also tried to remove the exception definition via weaveByType(OnExceptionDefinition.class).selectIndex(1).remove(); and add the OnException part via weaveAddFirst().onException(...).async...; but with the same result.

Appending a mocked error endpoint, however, is possible via f.e. weaveByToString("Log[io exception noticed]").after().to("mock:ioError");

So any tips for modifying onException blocks or the redeliveryDelay for unit tests are more than welcome.


@Edit: I also tried now to move the onException declarations above the route definition (from(...)) as suggested by the exception message and this was also the preferred case in Camel's exception samples. On doing this, however, all tests (even the working ones) fail as of a NullPointerException on context.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... }); as obviously the route itself can't be found anymore. I doubt that this is an IntelliJ issue as both classes are within the same project and therefore a modification of the route should be visible to the test-class.

Camel version in use: 2.13.0, IntelliJ IDEA 13.1.2


@Edit2: For some reason context.getRouteDefinitions("someQueueID") returns null if the OnException elements are defined outside of the from block, whereas the general route can be obtained via context.getRouteDefinitions().get(0) - though, the exception stating that the OnException part needs to be added as top-level element remains.


回答1:


When using the Java DSL, the id of the route is set using the .routeId() method, not .id() as you have coded above. That may help with your adviceWith concerns.

Instead of hard-coding the retry delay, a better approach would be to make the delay configurable using properties. Check out the documentation around the method useOverridePropertiesWithPropertiesComponent() on your CamelSpringTestSupport class.

EDIT

You don't have to weave the onException clause, simply state a new one. Here's a complete example:

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;
    }
}


来源:https://stackoverflow.com/questions/23737250/camel-route-testing-using-advicewith-with-onexception-definitions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!