Camel:无法动态添加路由

rap*_*apt 7 java apache-camel java-7 tomcat7

我正在使用Apache-Camel 2.15.2.

我正在尝试CamelContext动态添加路由,但我遇到了一个令我感到困惑的问题.

据我所知,我确实将路由添加到正确的路径CamelContext,看起来它们configure()被调用而没有抛出异常.但是当我尝试执行主路由时,我得到一个运行时异常告诉我,我动态添加的路由不存在.

这是我的代码的简化版本:

public class MainRouteBuilder extends RouteBuilder
{
    public static CamelContext camelContext;

    public static boolean returnLabel = true;

    public static RouteBuilder nestedRouteBuilder;

    @Override
    public void configure() throws Exception
    {
        System.out.println("Building main route!");
        System.out.println("Context: " + getContext());

        camelContext = getContext();

        from("direct:mainRoute")
        //3. I do not actually want to instantiate RouteContainer like this each time I call this route.
        //I would simply want to reuse a reference to an instance I created outside of configure()...
        .to(new RouteContainer().getMyRoute(2))
        ;

        returnLabel = false;

        //configure direct:myRoute.2

        includeRoutes(nestedRouteBuilder);      
    }

}

public class RouteContainer extends RouteBuilder
{
    public Route route;

    RouteContainer() {
        super(MainRouteBuilder.camelContext);
    }

    String getMyRoute(final int n) {

        if (MainRouteBuilder.returnLabel && route == null) {
            route = new Route() {

                @Override
                public void configure()
                {

                    System.out.println("Building nested route!");
                    System.out.println("Context: " + getContext());

                    from("direct:myRoute." + n)
                    .transform()
                    .simple("number: " + n)
                    .to("stream:out")
                    .process(new Processor() {
                        @Override
                        public void process(Exchange exchange) throws Exception {
                            Response response = new Response();
                            response.setStatus(Status.SUCCESS);

                            exchange.getOut().setBody(response);
                        }
                    });
                }               
            };
        }

//1. works:
        MainRouteBuilder.nestedRouteBuilder = this;

//2. does not work:
//      RouteContainer routeContainer = new RouteContainer();
//      routeContainer.route = this.route;
//      MainRouteBuilder.nestedRouteBuilder = routeContainer;

        return "direct:myRoute." + n;
    }


    @Override
    public void configure() throws Exception {
        if (route != null) {
            route.configure();
        }
    }

    public abstract static class Route {        
        abstract public void configure();
    }

}
Run Code Online (Sandbox Code Playgroud)

发送到direct:mainRoute工作的请求.在Camel启动期间,我在控制台中看到:

Building main route!
Context: SpringCamelContext(camel-1) with spring id org.springframework.web.context.WebApplicationContext:/sample-route
Building nested route!
Context: SpringCamelContext(camel-1) with spring id org.springframework.web.context.WebApplicationContext:/sample-route
Run Code Online (Sandbox Code Playgroud)

当我发送请求时direct:mainRoute,输出是:

{"status":"SUCCESS"}
Run Code Online (Sandbox Code Playgroud)

但是,如果我在上面注释掉(1)并取消注释(2),Camel会以相同的输出启动到控制台,但是当我发送请求时direct:mainRoute,路由的执行失败并出现异常:

org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://myRoute.2].
Run Code Online (Sandbox Code Playgroud)

澄清:我的问题是因为我实际上不想在RouteContainer每次调用它的路径时实例化,就像我在(3)中所做的那样.这就是我在第(2)点实例化它们并将Route实例插入其中的原因......

所以我想MainRouteBuilder看起来像这样:

public class MainRouteBuilder extends RouteBuilder
{
    public static CamelContext camelContext;

    public static boolean returnLabel = true;

    public static RouteBuilder nestedRouteBuilder;

    RouteContainer routeContainer = new RouteContainer();

    @Override
    public void configure() throws Exception
    {
        System.out.println("Building main route!");
        System.out.println("Context: " + getContext());

        camelContext = getContext();

        from("direct:mainRoute")
        .to(routeContainer.getMyRoute(2))
        //I may want to call it again like this:
        //.to(routeContainer.getMyRoute(3))
        ;

        returnLabel = false;

        //configure direct:myRoute.2

        includeRoutes(nestedRouteBuilder);      
    }

}
Run Code Online (Sandbox Code Playgroud)

我的假设是,嵌套路由可能direct:myRoute.2是错误的CamelContext,但控制台输出告诉我它不是这样.

知道我在这里做错了什么吗?

rau*_*ulk 8

路线配置!=路线执行

您似乎将路由配置与路由执行混淆.我们都去过那里;-)

配置RouteBuilder时MainRouteBuilder#configure(),该方法仅在Camel应用程序引导时执行一次,以便设置路由逻辑.DSL为路径(处理器,拦截器等)创建管道,就是路由运行时的内容.

指向带回家:每次交换都不会一遍又一遍地执行DSL .

换句话说,Camel没有按照你在(3)中指出的那样做.它不会new RouteContainer().getMyRoute(2)针对每个Exchange 执行.想一想:字节码configure()仅在配置Camel时执行,字节码实例化类的对象RouteContainer并调用getMyRoutewith参数2.生成的对象被送入SendProcessorto()DSL生成.

分析你的代码

现在,关于为什么你的代码不会产生你期望的结果.

你的状态保持有问题RouteContainer.每次调用时getMyRoute都会覆盖实例变量route.因此,您当前的代码不可能getMyRoute多次调用(使用不同的ns),然后includeRoutes在最后调用一次,因为只会添加最近生成的路由.

我也不喜欢Route用自己的类掩盖Camel 类,只是作为占位符,但这会带来你不需要的不同讨论.

更简单的解决方案

RouteContainer这是一个RouteGenerator创建路由并将direct:端点返回给调用者的类,而不是您的类.它跟踪内部的所有路线Set.

public class RouteGenerator {

    private Set<RouteBuilder> routeBuilders = new HashSet<>();

    public String generateRoute(final int n) {

        routeBuilders.add(new RouteBuilder() {
            @Override public void configure() throws Exception {
                System.out.println("Building nested route!");
                System.out.println("Context: " + getContext());

                from("direct:myRoute." + n)
                    .transform() .simple("number: " + n)
                    .to("stream:out")
                    .process(new Processor() {
                        @Override
                        public void process(Exchange exchange) throws Exception {
                            Response response = new Response();
                            response.setStatus(Status.SUCCESS);
                            exchange.getOut().setBody(response);
                        }
                    });
            }
        });

        return "direct:myRoute." + n;

    }

    public Set<RouteBuilder> getRouteBuilders() {
        return routeBuilders;
    }

}
Run Code Online (Sandbox Code Playgroud)

这是你的MainRouteBuilder,RouteGenerator它只实例化一次,并且可以生成任意数量的路径.

完成路由配置后,您只需迭代累积RouteBuilders并包含它们:

public class MainRouteBuilder extends RouteBuilder {

    public static CamelContext camelContext;

    public static RouteGenerator routeGenerator = new RouteGenerator();

    @Override
    public void configure() throws Exception {
        System.out.println("Building main route!");
        System.out.println("Context: " + getContext());

        camelContext = getContext();

        from("direct:mainRoute")
            .to(routeGenerator.generateRoute(2));


        for (RouteBuilder routeBuilder : routeGenerator.getRouteBuilders()) {
            includeRoutes(routeBuilder);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑:为什么你的选项(2)不起作用?

调试一段时间后,我意识到为什么你会看到这种效果.

Java教程中提取:

与实例方法和变量一样,内部类与其封闭类的实例相关联,并且可以直接访问该对象的方法和字段.

在(2)中,您创建一个Route在初始RouteContainer对象范围内的实例,充当外部对象.该Route对象将外部保留RouteContainer为其外部对象.的from()因此和随后的方法被调用,关于初始RouteContainer(RouteBuilder)对象,而不是在新RouteContainer以后创建,这是您提供给上部RouteBuilder(其被关联到CamelContext)之一.

这就是为什么你direct:myRoute.2没有被添加到Camel Context中,因为它是在不同的路由构建器中创建的.

另请注意(2)的控制台输出:

Building main route!
Context: CamelContext(camel-1)
Building nested route!
Context: CamelContext(camel-2)
Added!
Run Code Online (Sandbox Code Playgroud)

第二条路线被添加到不同的上下文中camel-2.这个新的上下文是由Camel懒惰地添加路由到旧的RouteBuilder创建的,而旧的RouteBuilder尚未与任何Camel Context相关联.

请注意,初始RouteContainer的Camel Context(在实例变量初始化中创建)是null,因为MainRouteBuilder.camelContext稍后您将分配该属性.

您可以通过添加以下println语句来查看如何使用两个不同的路由构建器:

内部路线#configure:

System.out.println("RouteContainer to which route is added: " + RouteContainer.this.hashCode());
Run Code Online (Sandbox Code Playgroud)

在MainRouteBuilder #conf中,就在之前includeRoutes:

System.out.println("RouteContainer loaded into Camel: " + nestedRouteBuilder.hashCode());
Run Code Online (Sandbox Code Playgroud)

使用(1),哈希码是相同的.使用(2),哈希码是不同的,清楚地表明存在两个不同的RouteBuilder(一个包含路由,一个加载到Context中,不包括路由).


来源:我是Apache Camel PMC成员和提交者.