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,但控制台输出告诉我它不是这样.
知道我在这里做错了什么吗?
您似乎将路由配置与路由执行混淆.我们都去过那里;-)
配置RouteBuilder时MainRouteBuilder#configure(),该方法仅在Camel应用程序引导时执行一次,以便设置路由逻辑.DSL为路径(处理器,拦截器等)创建管道,这就是路由运行时的内容.
指向带回家:每次交换都不会一遍又一遍地执行DSL .
换句话说,Camel没有按照你在(3)中指出的那样做.它不会new RouteContainer().getMyRoute(2)针对每个Exchange 执行.想一想:字节码configure()仅在配置Camel时执行,字节码实例化类的对象RouteContainer并调用getMyRoutewith参数2.生成的对象被送入SendProcessor该to()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)
调试一段时间后,我意识到为什么你会看到这种效果.
从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成员和提交者.