AdviceWith camel

http://camel.apache.org/advicewith.html

AdviceWith

Available as of Camel 2.1

AdviceWith is used for testing Camel routes where you can advice an existing route before its being tested. What adviceWith allows is to changes some factors on the route before the test is being run.

At current time you can advice an existing route by adding Intercept, Exception Clause etc. which then will apply for the route being advice.

For example in the route below we intercept sending a message to the mock:foo endpoint and detour the message.

public void testAdvised() throws Exception {
    // advice the first route using the inlined route builder
    context.getRouteDefinitions().get(0).adviceWith(context, new RouteBuilder() {
        @Override
        public void configure() throws Exception {
            // intercept sending to mock:foo and do something else
            interceptSendToEndpoint("mock:foo")
                    .skipSendToOriginalEndpoint()
                    .to("log:foo")
                    .to("mock:advised");
        }
    });

    getMockEndpoint("mock:foo").expectedMessageCount(0);
    getMockEndpoint("mock:advised").expectedMessageCount(1);
    getMockEndpoint("mock:result").expectedMessageCount(1);

    template.sendBody("direct:start", "Hello World");

    assertMockEndpointsSatisfied();
}
Recommendation
It is recommended to only advice a given route once (you can of course advice multiple routes). If you do it multiple times, then it may not work as expected, especially when any kind of error handling is involved.
The Camel team plan for Camel 3.0 to support this as internal refactorings in the routing engine is needed to support this properly.
Telling Camel you are using adviceWith
From Camel 2.9 onwards its recommended to override the isUseAdviceWith method and return true to tell Camel you are using advice with in your unit tests. Then after you have done the adviceWith, then you must start CamelContext manually. See further below for an example.

Using AdviceWithRouteBuilder

Available as of Camel 2.7

The AdviceWithRouteBuilder is a specialized RouteBuilder which has additional methods for advising routes. For example this allows you to manipulate the advised route, such as replacing a node with some other nodes.

The AdviceWithRouteBuilder offers the following extra methods

MethodDescription
mockEndpointsIs used to easily mock all endpoints. See more details and examples at Mock.
mockEndpoints(patterns)Is used to easily mock endpoints using a pattern. See more details and examples at Mock. See below for pattern matching. From Camel 2.10 onwards you can specify multiple patterns.
mockEndpointsAndSkip(patterns)Is used to easily mock endpoints using a pattern, and skip sending to the original endpoint. See more details and examples at Mock. See below for pattern matching. You can specify multiple patterns.
weaveById(pattern)Is used to select node(s) matching by id's, and weave in the following nodes. See below for pattern matching and examples.
weaveByToString(pattern)Is used to select nodes(s) matching by their toString representation, and weave in the following nodes. See below for pattern matching and examples.
weaveByType(Class)Camel 2.8: Is used to select node(s) matching by their class type (the classes from the org.apache.camel.model package), and weave in the following nodes. See below for examples.
weaveAddFirstCamel 2.8: Is a short hand to easily weave in the following nodes in the start of the route.
weaveAddLastCamel 2.8: Is a short hand to easily weave in the following nodes in the end of the route.
replaceFromWith(uri)Camel 2.9: To replace the route input with a new endpoint uri.

The pattern option is used for matching. It uses the same rules as the Intercept, which is applied in the following order:

  • match exact
  • match by wildcard
  • match by regular expression

For example to match exact you can use weaveById("foo") which will match only the id in the route which has the value "foo".
The wildcard is when the pattern ends with a * char, such as: weaveById("foo*") which will match any id's starting with "foo", such as foo, foobar, foobie and so forth.
The regular expression is more advanced and allows you to match multiple ids, such as weaveById("(foo|bar)") which will match both "foo" and "bar".

Using weaveById

The weaveById allows you to manipulate the route, for example by replacing a node with other nodes. The following methods is available:

MethodDescription
removeRemoves the selected node(s).
replaceReplaces the selected node(s) with the following nodes.
beforeBefore the selected node(s), the following nodes is added.
afterAfter the selected node(s), the following nodes is added.

For example given the following route:

Route
from("direct:start")
    .to("mock:foo")
    .to("mock:bar").id("bar")
    .to("mock:result");

Then let's go over the four methods to see how you can use them in unit tests:

Replace
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // weave the node in the route which has id = bar
        // and replace it with the following route path
        weaveById("bar").replace().multicast().to("mock:a").to("mock:b");
    }
});

In this example we replace the .to("mock:bar").id("bar") with the .multicast().to("mock:a").to("mock:b").
That means instead of sending the message to a "mock:bar" endpoint, we do a Multicast to "mock:a" and "mock:b" endpoints instead.

Remove
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // weave the node in the route which has id = bar and remove it
        weaveById("bar").remove();
    }
});

In the example above, we simply just remove the .to("mock:bar").id("bar").

Before
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // weave the node in the route which has id = bar
        // and insert the following route path before the adviced node
        weaveById("bar").before().to("mock:a").transform(constant("Bye World"));
    }
});

In the example above, we add the following nodes to("mock:a").transform(constant("Bye World")) before the node with the id "bar".
That means the message being send to "mock:bar" would have been transformed to a constant message "Bye World".

After
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // weave the node in the route which has id = bar
        // and insert the following route path after the advice node
        weaveById("bar").after().to("mock:a").transform(constant("Bye World"));
    }
});

In the example above, we add the following nodes to("mock:a").transform(constant("Bye World")) after the node with the id "bar".

Using weaveByToString

The weaveByToString also allows you to manipulate the route, for example by replacing a node with other nodes. As opposed to weaveById, this method uses the toString representation of the node(s) when matching. This allows you to match nodes, which may not have assigned ids, or to match EIP pattern.
You have to be a bit more careful when using this as the toString representation can be verbose and contain characters such as [ ] ( ) -> and so forth. That is why using the regular expression matching is the must useable.

The weaveByToString has the same methods as weaceById.

For example to replace any nodes which has "foo" you can do

Replace
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // weave nodes in the route which has foo anywhere in their to string representation
        // and replace them with the following route path
        weaveByToString(".*foo.*").replace().multicast().to("mock:a").to("mock:b");
    }
});

Notice that we have to use ".foo." in the pattern to match that "foo" is present anywhere in the string.

Using weaveByType

Available as of Camel 2.8

The weaveByToType also allows you to manipulate the route, for example by replacing a node with other nodes. As opposed to weaveById, and weaveByToString this method uses the class type of the node(s) when matching. This allows you to match EIP pattern by its type.

The weaveByToType has the same methods as weaceById and weaveByToString.

For example to remove a transform from the following route:

Route
from("direct:start")
    .transform(simple("Hello ${body}"))
    .log("Got ${body}")
    .to("mock:result");

You can do:

Remove
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // weave the type in the route and remove it
        weaveByType(TransformDefinition.class).remove();
    }
});

Using selectors

Available os of Camel 2.8

The following methods weaveById(pattern), weaveByToString(pattern) and weaveByType(Class) each match N+ nodes. By using optional selectors you can narrow down the nodes being used. For example if weaveByType(Class) returns 2 nodes. Then you can use a selector to indicate it should only select the first node.

SelectorDescription
selectFirstWill only select the first matched node.
selectLastWill only select the last matched node.
selectIndex(index)Will only select the n'th matched node. The index is zero-based.
selectRange(from, to)Will only select the matches node within the given range by index (both inclusive). The index is zero-based.

For example to remove the first .to node in route you can do as follows:

context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // only remove the first to node in the route
        weaveByType(ToDefinition.class).selectFirst().remove();
    }
});

Using weaveAddFirst / weaveAddLast

Available os of Camel 2.8

The weaveAddFirst and weaveAddLast is a shorthand to easily add nodes to the route. These methods can only add to an existing routes. If you want to manipulate the route, then there are plenty of methods as already shown on this page.

For example if you want to send a message to a mock:input endpoint you can do as follows:

context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // send the incoming message to mock:input
        weaveAddFirst().to("mock:input");
    }
});

Likewise if you want to easily send a message to a mock:output endpoint you can do as follows:

context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        // send the outgoing message to mock:output
        weaveAddLast().to("mock:output");
    }
});

You can of course combine those in the same advice with:

context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        weaveAddFirst().to("mock:input");
        weaveAddLast().to("mock:output");
    }
});

Replace from with another endpoint

Available as of Camel 2.9

You may have routes which consumes messages from endpoints which you want to substitute with another endpoint for easier unit testing. For example a JMS endpoint could be replaced with a SEDA or Direct for unit testing a route, as shown below where we replace the input of the route to a "seda:foo" endpoint:

context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
    @Override
    public void configure() throws Exception {
        replaceFromWith("seda:foo");
    }
});

Using mock endpoints

While routing messages, you may want to easily know how the messages was routed. For example you can let Camel mock all endpoints, which mean that when a message is sent to any endpoint, its first send to a mock endpoint, and then afterwards to the original endpoint. Then from your unit tests, you can setup expectations on the mock endpoints.

See more details at the section Mocking existing endpoints using the camel-test component at Mock.

Using isUseAdviceWith

Available as of Camel 2.9
It is recommended to override the method isUseAdviceWith and return true to instruct Camel that you are using adviceWith in the unit tests. Then in your unit test methods, after you have done the adviceWith you must start CamelContext by invoke the start method on the context instance. In the following we have an example. The route is using ActiveMQ to route messages. What we would like to do in a unit test is to test the route, but without having to set and use ActiveMQ. We do not have ActiveMQ on the classpath. So for that we need to advice the route and replace ActiveMQ with for example a SEDA endpoint instead.

isUseAdviceWith
public class IsUseAdviceWithJUnit4Test extends org.apache.camel.test.junit4.CamelTestSupport {

    @Test
    public void testIsUseAdviceWith() throws Exception {
        context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
            @Override
            public void configure() throws Exception {
                // replace the from with seda:foo
                replaceFromWith("seda:foo");
            }
        });
        // we must manually start when we are done with all the advice with
        context.start();

        getMockEndpoint("mock:result").expectedMessageCount(1);

        template.sendBody("seda:foo", "Hello World");

        assertMockEndpointsSatisfied();
    }

    @Override
    public boolean isUseAdviceWith() {
        // tell we are using advice with, which allows us to advice the route
        // before Camel is being started, and thus can replace activemq with something else.
        return true;
    }

    // This is the route we want to test
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                // we do not have activemq on the classpath
                // but the route has it included
                from("activemq:queue:foo")
                    .to("mock:result");
            }
        };
    }

}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值