mule in action翻译11 : 2.3 Mule 消息
2.3 Mule 消息
当一个消息传入mule, 它实际上是触发了一个事件(例如,可能是org.mule.api.MuleEvent的一个实例)。
这个事件不仅携带实际的消息本身(例如 一个org.mule.api.MuleMessage实例),而且还包含消息处理时
用到的上下文信息。
事件的上下文由多个不同对象的引用组成,包括安全证书(如果有的话)、处理请求的会话信息和mule的全局上下文信息。 通过上下文可以访问mule的所有内部构件。如图2.7所示 。
mule消息由不同的部分组成,如图2.8
1、payload 消息携带的主要内容数据
--感觉一个消息就像个拉集装箱的大货车,payload就是大货车拉的集装箱。
2、properties 包含消息的元数据信息
3、attachments 这部分是可选的且形式多样,用来支持多段消息(multipart messages)
4、exception payload 这部分是可选的, 用来保存事件处理过程中发生的任何的错误。
消息一般由消息源创建。当消息源接收到一个进入事件(如一个HTTP请求或进来一个JMS消息)或者
成功的轮询了资源(如一个文件系统或一个数据库)都会创建新的消息。
消息也可以通过编程的方式创建。
线程安全 默认mule保证在任意一个时间点只有一个线程可以修改消息。
线程间传递消息的常用的方式是消息复制。
图2.9展示了HTTP和email 的消息源创建的消息 。
如图2.9所示,消息payload的java 类型取决于消息“出身”。
byte数组,java.io.InputStream的实现类类型,或者字符串,是常见的payload类型。
一些特殊的payload类型,如 java.io.File对于File传输,也可以由mule消息进行传输。
payload不会捕获到整个事件的上下文信息,并在mule中传播。消息属性properties登场了。
2.3.1 消息属性(Message properties )
消息属性是元数据 ,提供mule携带的payload的上下文数据。
生成消息(典型的由是inbound endpoint生成)时会自动生成这些元信息。
当mule分发消息(典型是由outbound endpoint发出)到一个特定目的地时, 也会读取消息属性。
在消息处理过程中 或 进行消息路由时都会使用到消息属性。
消息属性有一个名字(String),一个值(object)和一个范围(enumeration)组成。
消息属性包含下面消息:
1、 一般的传输的元消息 保存在消息属性。想想 http请求头,或JMS的消息属性。
一些传输协议如TCP ,不会创建任何的元信息。
2、 mule特定的传输元信息 用来存储上下文信息。例如 mule收到所有的HTTP请求的请求路线
会以 http.request.path的名字保存起来。
mule的多数消息传输过程都有outbound endpoints 。这些outbound endpoints对消息属性是“敏感”的。
例如SMTP传输会自动识别和发送email所需要的元信息相关的所有的 消息属性,如subject或toAddresses。
传输属性 一些传输对属性名字有要求。若你试图使用不符合JMS规范的属性时, 它会进行明确的提示。
不幸的是,并没有一个权威的包含所有属性的列表; 对每个协议你都需要查阅其在线文档。
另外,我们建议在开发模式下使用logger消息处理器,以显示传输用到的属性消息。
2.3.2 理解属性作用域(property scopes)
属性在一个范围内有效,但在另一个范围内是无效的。
这意味着,消息属性不像payloads那样,它在mule内不会一直被自动携带。
这个“范围”是有边界的,属性并不会被一直传递下去。
这是边界是如何定义的?
流定义了属性作用域的边界。更确切的说,流的inbound endpoints 和 outbound endpoints 是消息属性传递的边界。
图2.10 说明了不同的属性作用域 以及相关的边界。
四种作用域的属性 :
1、Inbound property 由消息源创建, 这些属性对最终用户是只读的。
Inbound properties可以被outbound endpoints清除;发生这种情况时,
你需要修改你的程序了。
2、Outbound property 使用set-property, message-properties-transformer 创建,
需要指定scope是outbound 。
也可通过编码创建。
Outbound property由outbound endpoints进行识别。
3、Invocation property 使用 set-variable, message-properties-transformer创建,
需要指定scope是invocation。
或通过编码生成。
Invocation property主要作为流变量。
4、Session property 由消息源创建 ,
使用 set-variable, message-properties-transformer 创建,
需要指定scope是session。
也可通过编码创建。
这个作用域一定是对于"飞着的消息"使用(没看明白原文:
this scope is bound to the in-flight message),
可以跨多个流(想一下java的threadlocal 机制,这里差不多是MessageLocal)
边界交叉 跨作用域时使用 copy-properties元素来复制属性,
例如传递一个 inbound property 到 outbound property , 或存储到一个 会话作用域。
目前为止我们只是关注请求阶段的属性。响应阶段是什么样 ?在响应阶段是反过来的。
Inbound endpoints查找outbound作用域的属性,去传递给调用者。
请求-响应的outbound endpoints创建inbound属性 。
只有流和会话变量的行为 在请求和响应阶段都是相同的,
只要它们被保存下来且在消息处理过程中国没有被影响。
列表2.11 展示了 在响应阶段怎么设置HTTP content-type。
如何创建一个属性,才能在inbound endpoint返回给调用者时使用?
秘密在于set-property创建了 outbound 作用域的属性。
Listing 2.11 Adding a header to the outbound scope adds it to the response
<flow name="responseHeader"> <http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8080" path="json/data" /> <component class="com.prancingdonkey.data.JsonDataFetcher" /> <response> <set-property propertyName="Content-Type" value="application/json" /> </response> </flow>
响应阶段属性的传递 在响应阶段要把一个消息属性从一个outbound endpoint返回给调用
inbound endpoint 的调用者,需要把它从inbound拷贝到 outbound 。
看另外一个设计多个作用域的例子。列表 2.12显示了一个接受用户输入xml的例子。
它使用XPath表达式 提取出ID ,并保存为一个名字为useId的流变量。
这个流变量随后有两个地方使用:
在一个HTTP outbound endpoint使用它创建一个 REST 资源URI。
在响应添加一个名字为 X-User-ID的属性。
Invocation scope在响应阶段仍是有效的。
在inbound阶段设置的流变量在响应阶段仍然有效。
Listing 2.12 A flow that sets a variable and uses it in two places
<flow name="flowVarPersistence"> <vm:inbound-endpoint path="user.fetch" exchange-pattern="request-response" /> <response> <set-property propertyName="X-User-ID" value="#[userId]" /> </response> <set-variable variableName="userId" value="#[xpath('/user/@id').value]" /> <http:outbound-endpoint address="http://${internalApiBaseUri}/api/users/#[userId]" method="GET" exchange-pattern="request-response" /> </flow>
可访问但不拥有 尽管流变量和会话变量都可被mule消息的属性访问器访问,
但他们实际上却是属于mule的Event 和 Session 对象的。
为了方便,mule消息把其引用分享到支持这两个作用域的map中。
尽管不经常使用, attachments也是 mule消息的重要成员。
2.3.3 使用消息附件( message attachments)
附件用来支持额外的数据,按严格语义来讲它不是元信息 ,它更像payload的替代品或补充。
一个附件由一个名字定义、一个数据源(一个 javax.activation.DataHandler )及一个作用域 定义。
附件的作用域限制在 inbound和outbound,和属性作用域一样有相同的流边界。
附件主要用来支持inbound的email的分段消息(由POP或IMAP创建) 和
outbound email 消息(使用 SMTP传输)。
inbound 附件由email endpoint 直接创建,而outbound endpoint的附件需要以编程方式创建。
下面的列表展示了一个流, Prancing Donkey 公司用它接收email订单。
Listing 2.13 Extracting message attachments with an expression transformer
<flow name="email-order-processor"> <!-- 1 通过一个在别地配置的全局的 endpoint接收email--> <inbound-endpoint ref="email-orders" /> <!-- 2 提取当前消息所有的附件 --> <expression-transformer expression="#[message.inboundAttachments.values()]" /> <!-- 3 拆分附件列表到单独的每个消息--> <collection-splitter /> <!-- 4 发送一个独立订单消息以进行处理--> <outbound-endpoint ref="pdf-orders" /> </flow>
在注释2处,这里使用表达式转换器提取所有的附件,并把当前消息的payload内容替换为所提取的附件列表
(inboundAttachments 是 java.util.Map --保存附件的name和value)。
在注释1后,存储在消息payload中的消息体被丢弃,内容被替换成了附件。
在注释3处,把附加列表拆分成独立的消息--因为允许客户在一个email中附加几个发票。
在注释4处,上步被拆分的形成的每个消息 都将由这里调用的服务进行处理。