Apache Camel:在路径末端访问请求和回复消息

时间:2012-09-25 16:45:21

标签: apache-camel

我想在路线的最后处理请求和响应消息。但是,我没有看到如何访问原始请求消息的方法。

我有一种可怕的感觉,我正在努力解决一些基本概念。

这是DSL中的一个简单示例路由,用于概述我的问题(为整个上下文启用了streamCaching):

from("activemq:queue:myQueue")
.to("log:" + getClass().getName() + "?showOut=true")
.to("http://localhost:8080/someBackend")
.log("Now in.Body returns this: ${in.body} and out.Body this: ${out.body}")
.to("log:" + getClass().getName() + "?showOut=true");

以下是我的日志的摘录(为了更好的阅读而编辑的换行符)。可以看出,一旦http服务器回复,原始的SOAP消息就会丢失,而SOAP响应对象则存储在消息的inBody中。

2012-09-25 17:28:08,312 local.bar.foo.MyRouteBuilder INFO - 
    Exchange[ExchangePattern:InOut, BodyType:byte[],
    Body:<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Header /><env:Body><urn:someRequest  xmlns:urn="http://foo.bar.local/ns"></urn:someRequest></env:Body></env:Envelope>, 
    Out: null]
2012-09-25 17:28:08,398 org.apache.camel.component.http.HttpProducer DEBUG - 
    Executing http POST method: http://localhost:8080/someBackend
2012-09-25 17:28:09,389 org.apache.camel.component.http.HttpProducer DEBUG - 
    Http responseCode: 200
2012-09-25 17:28:09,392 route2 INFO - 
    Now in.Body returns this: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:someResponse xmlns:ns2="http://foo.bar.local/ns"</ns2:someResponse></soap:Body></soap:Envelope>
    and out.Body this: 
2012-09-25 17:28:09,392 local.bar.foo.MyRouteBuilder INFO - 
    Exchange[ExchangePattern:InOut,  
    BodyType:org.apache.camel.converter.stream.InputStreamCache,
    Body:[Body is instance of org.apache.camel.StreamCache],
    Out: null]

我原本希望在整个路线上保留in.body和out.body吗?

我正在考虑的其他解决方案:

  • 使用Correlation Identifier模式关联请求和回复。但这会保留邮件正文吗?此外,我的请求/回复消息没有唯一的关联标识符。
  • 编写一个自定义bean,它执行对http后端的调用,处理请求和回复对象(但这基本上是一个无骆驼解决方案,重新发明轮子,因此不是首选)

已经失败的方法:

我尝试在路线的末尾使用像这样的处理器访问原始请求消息,但没有成功:

 process(new Processor() {
   @Override
   public void process(Exchange exchange) throws Exception {
      Message originalInMessage = exchange.getUnitOfWork().getOriginalInMessage();
         logger.debug(originalInMessage.getBody(String.class));
         logger.debug(exchange.getIn().getBody(String.class));
   }
 });

感谢您的帮助

4 个答案:

答案 0 :(得分:15)

只需将邮件的原始正文存储在标题或属性中,并在最后检索它:

from("activemq:queue:myQueue")
.setProperty("origInBody", body())
.to("http://localhost:8080/someBackend")

在http调用之后,您可以访问属性origInBody。

答案 1 :(得分:13)

首先,本文非常清楚地展示了骆驼的进出工作方式:http://camel.apache.org/using-getin-or-getout-methods-on-exchange.html

通常,out消息并不总是被使用,而是在每个步骤中从消息中复制。

在您的情况下,您希望原始邮件留在路线的尽头,您可以继续使用Enrichment EIP。 http://camel.apache.org/content-enricher.html

你的路线是这样的:

public class MyAggregationStrategy implements AggregationStrategy {
  public Exchange aggregate(Exchange orig, Exchange httpExchange){
    // if you want to check something with the Http request, you better do that here 
    if( httpExchange is not correct in some way ) 
       throw new RuntimeException("Something went wrong");

    return orig;
  }
}

AggregationStrategy aggStrategy = new MyAggregationStrategy();

from("activemq:queue:myQueue")
  .enrich("http://localhost:8080/someBackend",aggStrategy)
  .//keep processing the original request here if you like, in the "in" message

答案 2 :(得分:3)

骆驼最大的问题之一就是易于滥用。。正确使用它的最佳方法是根据EIP进行思考:骆驼的主要目标之一是在其DSL中实现EIP

Here is a list of EIP

现在考虑一下。您到底想要请求和响应是什么用途?记录,汇总...?对于日志记录,correlationId应该足够,因此我认为您需要它来基于请求和代理响应创建响应。如果那是您想要的,则可以执行

之类的操作
from("direct:receiveRequest")
   .enrich("direct:proxyResponse", new RequestAndResponseAggregationStrategy())

您将有机会合并您的请求(在oldExchange中)和您的响应(在newExchange中)。

在我对Christian Schneider的所有应有的尊重的前提下,我确实认为将请求放置在可以稍后重用的属性中的想法很糟糕。这样,您会在路线之间产生副作用。如果您的路线是另一条路线的子路线,则可能会删除其属性。如果您存储它以便以后放回去,也许您应该做类似

的操作
from("direct:receiveRequest")
    .enrich("direct:subRouteToIgnoreResponse", AggregationStrategies.useOriginal())

我自己做了太多次的设计真的很糟糕:

from("direct:receiveRequest")
    .to("direct:subroute")

from("direct:subroute")
    .setProperty("originalBody", body())
    .to("direct:handling")
    .transform(property("originalBody")

这将导致“属性/标头死机”,并导致只是连续调用处理器的路由。

如果您想不出解决EIP问题的方法,则应该只使用骆驼来访问其组件。例如:

from("http://api.com/services")
   .to(new SomeNotTranslatableToEIPProcessor())
   .to("ftp://destination")

但是请不要忘记,这些组件有自己的目标:在类似行为(例如,基于时间的轮询使用者)之间创建通用的抽象。如果您有一个非常特定的需求,尝试将骆驼组件弯曲到这个特定需求可能会导致大量的代码不容易维护。

不要让骆驼成为您的Golden Hammer anti-pattern

答案 3 :(得分:0)

我经常使用一种聚合策略,该策略会保留旧的交换并将富集的结果放入标头中:

ComboBox

现在您可以像这样使用它:

import org.apache.camel.Exchange;
import org.apache.camel.processor.aggregate.AggregationStrategy;

public class SetBodyToHeaderAggregationStrategy implements AggregationStrategy {

    private String headerName = "newBody";

    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        oldExchange.getIn().setHeader(headerName, newExchange.getIn().getBody());

        return oldExchange;
    }

    @SuppressWarnings("unused")
    public void setHeaderName(String headerName) {
        this.headerName = headerName;
    }

}