我正在使用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();
}
}
发送到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
当我向direct:mainRoute
发送请求时,输出为:
{"status":"SUCCESS"}
但是,如果我在上面注释掉(1)并取消注释(2),Camel会以相同的输出启动到控制台,但是当我向direct:mainRoute
发送请求时,路由的执行失败除了:
org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://myRoute.2].
澄清:我的问题是因为我实际上不希望每次调用它的路径时都不会实例化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);
}
}
我的假设是,嵌套路由direct:myRoute.2
可能是在错误的CamelContext
中创建的,但控制台输出告诉我它不是这样。
知道我在这里做错了吗?
答案 0 :(得分:8)
您似乎将路由配置与路由执行混淆。我们都去过那里; - )
在MainRouteBuilder#configure()
中配置RouteBuilder时,该方法仅在Camel应用程序引导时执行一次,以便设置路由逻辑。 DSL为路径(处理器,拦截器等)创建管道,并为创建路由运行时。
要点回家:每个Exchange都反复执行DSL 。
换句话说,Camel没有按照你在(3)中指出的那样做。它不会为每个Exchange执行new RouteContainer().getMyRoute(2)
。考虑一下:configure()
的字节码仅在配置Camel时执行,字节码实例化类RouteContainer
的对象,并使用参数getMyRoute
调用2
。生成的对象将被输入to()
DSL生成的SendProcessor
。
现在,关于为什么你的代码没有产生你期望的结果。
RouteContainer
的状态保存存在问题。每次调用getMyRoute
时,都会覆盖实例变量route
。因此,您当前的代码无法多次调用getMyRoute
(使用不同的n
s),然后在最后调用includeRoutes
一次,因为只会添加最近生成的路由
我也不喜欢用你自己的类来掩盖Camel Route
类,只是作为占位符,但这会带来你不需要的不同讨论。
这是一个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;
}
}
这是你的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);
}
}
}
调试一段时间后,我意识到为什么你会看到这种效果。
与实例方法和变量一样,内部类是关联的 使用其封闭类的实例并可直接访问它 对象的方法和领域。
在(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!
第二条路线被添加到不同的上下文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());
在MainRouteBuilder #confin中,就在includeRoutes
之前:
System.out.println("RouteContainer loaded into Camel: " + nestedRouteBuilder.hashCode());
使用(1),哈希码是相同的。使用(2),哈希码是不同的,清楚地表明存在两个不同的RouteBuilder(一个包含路由,一个加载到Context中,不包括路由)
来源:我是Apache Camel PMC成员和提交者。