Mule ESB学习【转-结合了网络上的一些资源】

时间:2022-03-16 13:43:20

1.SOA标准之一:SCA架构思想

SOA在Java领域有两套标准:一个是SUN推出的JBI(没有得到BEA和IBM的承认),另外一个是:IBM和BEA等公司推出的SCA和SDO标准。

JBI之关注Java组件只处理Java组件的集成。

SCA实现了业务组件和传输协议的分离,可以处理各种平台组件的集成。

SDO可以的*读取各种不同数据源的数据。

另外,BPEL本质上是一种集成Web Service服务的语言,也可以算作为SOA的一部分。

在此,详细介绍SCA的架构思想。

1.1      认识SCA

SCA(Service Component Architecture)中文翻译为“服务组件架构”,是一种全新的软件架构思想。

SCA中,最重要的一个概念是Service----服务,它的内涵式独立于具体的技术。因此,SCA不会称之为 Java组件架构,或Web Service 组件架构。所谓的具体技术,主要有两层含义:一是程序语言,而是传输协议。

现有的组件是和传输协议紧密耦合的。比如EJB组件采用的是RMI传输协议,Web Service组件采用的是SOAP传输协议。SCA组件则能*地绑定各种传输协议。

SCA是对目前组件编程的进一步升华,其目标是让服务组件能*绑定各种传输协议,集成其他的组建与服务。

SCA与传统的业务组件最大区别在于SCA实现了两个功能:一是组件和传输协议的分离,二是接口和实现语言的分离。

SCA的本质是一种软件架构思想,SCA架构是独立于程序语言的SOA架构。

SCA的目标是创建一个可集成服务组件的运行环境。

我什么需要SCA?答案:集成的需要。

先看没有使用SOA技术的系统的集成的情况,需要相互约定和暴露接口。需要编写集成的客户端调用代码。调用方和被调用方要“知彼知己”才能很好的集成,而这又都带来高昂的代价和复杂度。

使用SCA的好处:组件之间处于一种松耦合的状态,不需要在自己的代码中加入对方组件的接口代码。

1.2      认识SCA容器

SCA是一种思想,SCA思想的具体实现是SCA标准和SCA的容器环境。

SOA容器也分JBI容器、SCA容器等。SCA容器也是SOA容器总称的一种,通常都单独称SCA容器,而直接泛称SOA容器。这里为了区别与别的SOA容器开来,而称之为SCA容器。

SCA容器实现了将复杂的服务组件集成过程隐藏在容器内部,开发者之需要按照SCA的标准去开发和集成服务,最终部署到SCA的容器里面即可。

SCA容器的实现很复杂,有关其容器的组成与架构也是一种商业秘密。开发人员只需要关系如何遵循SCA标准去开发和集成服务组件即可。

为了更好去实现SCA架构,理解SCA服务组件概念的内涵和外延对开发者来说是非常重要的。

为了更清晰的认识SCA的概念和原理,下面先给出一个简单的SCA例子,以获得对SCA有个感性的认识。

1.3      SCA实现的Hello World组件实例

完整SCA组件Hello World实例包含两部分内容:

1.3.1               SCA组件的服务端代码

服务端代码包括三个部分:

服务接口,一个Java的Interface----HelloService.java。

服务实现,HelloService接口的实现类----HelloServiceImpl.java。

SCA的服务组件配置文件:Hello.composite。

HelloService.java

/** 
* 服务接口 
*/ 
public interface HelloService { 
        String getHello(String
username); 
}

HelloServiceImpl.java

package hello; 
/** 
* 服务实现 
*/ 
public class HelloServiceImpl implements HelloService

        public String getHello(String username) { 
                return "Hello " + username + "! This is a SCA program!"; 
        } 
}

Hello.composite

<!-- SCA的服务组件配置文件 --> 
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="Hello"> 
        <component name="HelloServiceComponent"> 
                <implementation.java class="hello.HelloServiceImpl"/> 
                <property name="username" type="xsd:string" default="World"/> 
        </component> 
</composite>

1.3.2              
SCA组件的客户端代码

package hello; 
/** 
* SCA的客户端调用 
*/ 
public class HelloSCAClient { 
        public static void main(String[] args) { 
                SCADomain
scaDomain = SCADomain.newInstance("Hello.composite"); 
                HelloService
helloService = 
                                scaDomain.getService(HelloService.class, "HelloServiceComponent"); 
                String
msg = helloService.getHello("vcom"); 
                System.out.println(msg); 
                scaDomain.close(); 
        } 
}

从客户端的调用代码来看,客户端需要知道服务端组件提供了什么服务,实现了什么纯Java接口。而不需要接口后面的实现细节。

从上面的开发过程可以看到,开发SCA组件,客户端和服务端都不需要知道技术细节。

客户端调用其他组件的时候,只通过一个简单的SCADomain实例,直接获取服务的实现实例就可以调用,而负责的调用和实现都交给了SCA运行环境。

通过上面的组建实例,也可以看出:服务组件是SCA架构的基本单元,SCA是以服务组件为基本单元来进行集成的。下面将介绍各种服务组件的概念。

1.4     
服务组件

1.4.1              
概念

服务组件准确讲没有确切的概念,它更贴近于一件实实在在的物品,只能从他的形状、组成、结构、功能、状态、属性等侧面来描述它。

服务组件是SCA里面最基本的功能单元,它主要包括接口、实现、引用、属性等部分。可以从一下侧面来描述服务组件。

a)、是在一个模块(Composit)内的通过配置生成的一个实现的实例。

b)、多个组件可以用同一个实现(思考:一个Java的对象可以同时实现多个接口)。

c)、提供服务和消费服务(组件可以调用别的组件的服务)。

d)、通过配置来实现对象的属性值(配置节点为property)。

e)、组件通过连线(Wire)来设置服务引用。连线可以连接到别的组件的服务,也可以连接到模块的引用(模块的概念后面会详细讲述)。

1.4.2 服务组件的组成部分

服务组件的组成包含四个部分:服务、组件实现、引用、创建属性。

a)、服务(Service),用来让其他组件调用。是一个接口。如果是基于Java的SCA,它就是Java的接口;也可以是WSDL的ProtType接口,目前只有这两种形式。

b)、组件实现(Implementation),实现所创建的服务,对Java来说,就是接口的实现类。

c)、引用(Reference),一个组件可能需要调用其他组件,需要创建于igeqita组件的引用。对Java来说,就是其他组件的Java接口。

d)、属性(Property),对组件实现的一种属性参数注入。

对一个服务组件来说,服务和实现是必须的,引用和属性是非必须的。

1.5     
服务模块

SCA是通过模块(Composite)将SCA组件集成在一起的。

SCA的模块是实际上是将SCA组件(作为零件)重新组合集成度更高的组建,从整体看来SCA模块和SCA组件的结构式一致的。从构成组件的“零件”角度看,SCA模块是用了组件作为零部件重新组装为新的组件(模块)。

模块从整体上也是个组件。

模块是通过SCA的配置文件配置组装形成的,不需要程序的硬编码。

提升(Promote):就是将组件的接口、属性、或引用装配为模块的对应的接口、属性或引用。

连线(Wire):就是在模块内部,组件之间的调用关系。比如组件A的实现调用了组件B,那么组件AB间就存在一个连线。

当组件之间需要调用的时候,由于目前组件(如EJB、WS、JMS)传输协议的多样化,这样在相互的调用的时候,需要将绑定不同的协议去调用。这里尽可能避免让人迷惑而又没有价值的绑定(Binding)一词的概念。

1.6     
服务子系统

在一个大的项目里面,可能会有很多服务模块,多个服务模块之间如果需要相互调用,那么就可以将多个服务模块通过WS或者JMS等技术绑定在一起,形成服务子系统。

理解了模块的概念,就不难理解服务子系统了。

1.7     
SCA与JBI的异同

相同点:

目的是一样的:都是为了集成。

大致方向一样:都是为了将服务和传输协议解耦。

不同点:

SCA以接口作为切入点,从组件接口层将传输协议和接口实现解耦,是从编程的角度出发,一种全新的编程模型。

JBI是以请求消息和相应消息作为切入点,在集成时将消息和传输协议解耦,形成一种与传输协议无关的标准消息,这样形成一种全新的区别于现有应用服务器的集成容器,是从容器的角度出发,一种全新的容器模型。

2.Mule ESB的基本概念

2.1 Model

Model表示托管各个服务的运行时环境。

2.2 Service

Service是用来处理服务请求的基本单位,它调用各个组件进行服务请求的处理。

2.3 Transport

Transport管理消息的接收和发送,数据转换的过程也是在Transport中通过调用Transformer完成的。

2.4 Connector

Connector用于管控特定协议的使用,如HTTP Connector、JMS Connector等。

2.5 End-Point

Endpoint用于表示一种协议的特定使用方式,如listening/polling、从中读取、向指定地址写入等,定义了发送和接收消息的通道。Endpoint控制的是底层的实体在Connector中如何被使用。

Endpoint定义于Inbound和Outbound Router中。

2.6 Transformer

Transformer用于转换消息的内容。

2.7 Router

Router使用Filter基于消息中的属性信息进行消息的分发。

Router在Service中的位置决定了Router的性质(inbound、outbound和response)和担任的角色(pass-through、aggregator等)。

2.8 Component

Component是Service的核心部件,是Service的业务逻辑的实现。

Component可以是JavaClass(POJO、spring Bean)、Web Service、Script等。

Component可定义自己的生命周期:initialise、start、stop、dispose,不过需要实现Mule的LifeCycle接口。Mule 3.0版本开始提供@PostConstruct和@PreDestroy的注解,对应生命周期的initialise和dispose阶段,不需要实现Mule的LifeCycle接口了。

2.9 Flow(@since 3.0)

Flow是Mule 3.0新引入的,包含一个消息源(Message Source)和多个消息处理器组成的处理器链。

3. Mule ESB配置模式

Mule
3.0版本提供了“pattern”的机制。Pattern总结了实际使用过程中的常见场景,以简化的服务配置方式提供。

3.1简单服务模式(simple service pattern)

简单服务模式用于简化同步服务调用的配置,对应消息传递方式中的请求-响应方式。

简单服务模式通过simple-service 元素配置,主要的元素属性包括:

属性

说明

address

服务监听的地址,如vm:in

component-class

Component的实现类

type

direct: 默认

jax-ws: 将component暴露为soap式的web service(component必须基于jax-ws的注解),address一般为Http Transport;

jax-rs: 将component暴露为rest式的web service(component必须基于@Path的注解),address一般为Http或Servlet
Transport

代码示例:

<simple-service
name="simple-service" address="vm://simple.in"     
component-class="demo.mule.umo.Echo" />

Mule针对服务请求接入可以做额外的处理,比如增加Transformer配置进行数据转换。

3.2 桥接模式(bridge pattern)

桥接模式用于在inbound endpoint和outbound endpoint之间建立直接连接,不需要component提供业务逻辑。

桥接模式通过bridge元素配置,主要属性包括:

属性

说明

inboundAddress

服务请求接入地址

outboundAddress

服务接出的实际地址

exchange-pattern

request-response: 默认,返回处理结果

one-way: 单向

transacted

true: 在向outbound endpoint分发时使用事务

false: 不使用事务

代码示例:

<bridge
name="queue-to-topic" transacted="true"
inboundAddress="jms://myQueue"         
outboundAddress="jms://topic:myTopic" />

Mule在接入、接出的过程中可以做额外的处理,比如增加Transformer配置进行数据转换。如果使用事务控制,对于异构的协议之间的事务需要有支持XA的事务控制器。

3.3 校验器模式(validator pattern)

校验器模式通过定义一个校验过滤器过滤服务请求,并同步返回ACK(ACKnowledge)或NACK(Not Acknowledge)结果。通过校验的服务请求被异步分发给处理方。

校验器模式通过validator元素配置,主要属性包括:

属性

说明

inboundAddress

服务请求接入地址

outboundAddress

服务接出地址

ackExpression

表达式,用于构建服务请求被接收时的信息

nackExpression

表达式,用于构建服务请求被拒绝时的信息

errorExpression

@since 3.0.1表达式,用于构建在服务请求分发出错时的信息

validationFilter-ref

过滤器的引用,也可以使用子元素指定;用于确定服务请求是否被接收

代码示例:

<validator
name="integer-validator" inboundAddress="vm://validator.in"
         ackExpression="#[string:GOOD:#[message:payload]@#[context:serviceName]]"         
nackExpression="#[string:BAD:#[message:payload]@#[context:serviceName]]"         
outboundAddress="vm://test-service.in">      <payload-type-filter
expectedType="java.lang.Integer" />  </validator>

注:Mule的表达式后续补充。

3.4 web服务代理模式(web service proxy pattern)

Web服务代理模式用于将Web Service请求直接转发至远程目标Web Service服务端,Mule本身不提供实际的Web Service。

Web服务代理模式通过ws-proxy元素配置,主要属性包括:

属性

说明

inboundAddress

Mule对外提供的地址

outboundAddress

Web Service的实际地址

代码示例:

<ws:proxy
name="ws-proxy"         
inboundAddress="http://localhost:7006/services/Echo"         
outboundAddress="http://localhost:8000/services/Echo?method=echo">  </ws:proxy>

Mule在转发的过程中可以做额外的处理,比如增加Transformer配置进行数据转换。

4. Mule ESB配置

4.1 基本实现:

4.1.1 实现命令行输入发送email

为了能在命令行接受输入,需要配置一个输入输出连接器

<stdio:connector name="SystemStreamConnector"  promptMessage="Please enter email content(email address, contents): " messageDelayTime="1000" />

4.1.2 配置一个UMO,把输出的内容放入一个列队

<!-- any number of endpoints can be added to an inbound router -->    
    <inbound>    
        <stdio:inbound-endpoint system="IN" />    
    </inbound>    
    <outbound>    
        <pass-through-router>    
            <vm:outbound-endpoint path="content" />    
        </pass-through-router>    
    </outbound>    
</service>

outbound节点的配置, 把输入的内容(String) 路由到一个叫“content”的queue中, 此queue为jvm中的内存队列。

4.1.3 配置一个UMO,发送email

<service name="EmailBridge">    
            <inbound>    
                <vm:inbound-endpoint path="content" />    
            </inbound>    
            <outbound>    
                <pass-through-router>    
                    <smtps:outbound-endpoint user="lcllcl987"    
                        password="yourpassword" host="smtp.gmail.com"    
                        transformer-refs="ContentToEmail StringToMimeMessage"    
                        connector-ref="emailConnector" from="*@mycomp.com.cn"    
                        subject="test for mule email bridge!" />    
                </pass-through-router>    
            </outbound>    
 </service>

其中inbound的配置为contentUMO的outbound, contentUMO和EmailBridge这个两个UMO通过名称为“content”的queue连接起来, 实现通讯。EmailBridge接收到输入后, 会依次通过ContentToEmail, StringToMimeMessage两个transformer进行内容的转换。

BTW:为了在mule中使用smtp, 需要在xml的namespace中声明:

xmlns:smtps="http://www.mulesource.org/schema/mule/smtps/2.1"

mule有很多对于具体协议的transport实现,每一个transport的实现作为一个jar包存在(比如mule-transport-email-2.1.2.jar), 在jar中的META-INF/spring.schemas文件中, 写明了xsd文件的对应关系, META-INF/sping.handers配置了相关命名空间的handle class, 可以据此在mule的配置文件中声明命名空间.
        完整的mule配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>    
<mule xmlns="http://www.mulesource.org/schema/mule/core/2.1"    
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
xmlns:spring="http://www.springframework.org/schema/beans"    
xmlns:stdio="http://www.mulesource.org/schema/mule/stdio/2.1"    
xmlns:vm="http://www.mulesource.org/schema/mule/vm/2.1"    
xmlns:smtps="http://www.mulesource.org/schema/mule/smtps/2.1"    
xsi:schemaLocation="     
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd     
   http://www.mulesource.org/schema/mule/core/2.1 http://www.mulesource.org/schema/mule/core/2.1/mule.xsd     
   http://www.mulesource.org/schema/mule/stdio/2.1 http://www.mulesource.org/schema/mule/stdio/2.1/mule-stdio.xsd     
   http://www.mulesource.org/schema/mule/vm/2.1 http://www.mulesource.org/schema/mule/vm/2.1/mule-vm.xsd     
   http://www.mulesource.org/schema/mule/smtps/2.1 http://www.mulesource.org/schema/mule/smtps/2.1/mule-smtps.xsd">    
    
<description>    
    This is a simple component example that demostrates how to send a e-mail     
</description>    
<stdio:connector name="SystemStreamConnector"    
    promptMessage="Please enter email content(email address, contents): " messageDelayTime="1000" />    
    
<!-- This configures an extra setting if you're using GMail's SMTP -->    
<custom-connector name="emailConnector"    
    class="co.mule.mail.SmtpConnector" />    
<custom-transformer name="ContentToEmail"    
    class="co.mule.mail.ContentToEmailTransformer" />    
<custom-transformer name="StringToMimeMessage"    
    class="org.mule.transport.email.transformers.StringToEmailMessage" />    
<!--   The Mule model initialises and manages your UMO components    -->    
<model name="myEmail">    
    <!--     
        A Mule service defines all the necessary information about how your components will     
        interact with the framework, other components in the system and external sources.     
        Please refer to the Configuration Guide for a full description of all the parameters.     
    -->    
    <service name="contentUMO">    
        <!-- any number of endpoints can be added to an inbound router -->    
        <inbound>    
            <stdio:inbound-endpoint system="IN" />    
        </inbound>    
        <outbound>    
            <pass-through-router>    
                <vm:outbound-endpoint path="content" />    
            </pass-through-router>    
        </outbound>    
    </service>    
    <service name="EmailBridge">    
        <inbound>    
            <vm:inbound-endpoint path="content" />    
        </inbound>    
        <outbound>    
            <pass-through-router>    
                <smtps:outbound-endpoint user="lcllcl987"    
                    password="yourpassword" host="smtp.gmail.com"    
                    transformer-refs="ContentToEmail StringToMimeMessage"    
                    connector-ref="emailConnector" from="*@mycomp.com.cn"    
                    subject="test for mule email bridge!" />    
            </pass-through-router>    
        </outbound>    
    </service>    
</model>    
</mule>

相关class如下:
自定义消息转换器:

public class ContentToEmailTransformer extends AbstractTransformer     
{     
    @Override    
    protected Object doTransform(Object src, String encoding) throws TransformerException     
    {     
        String body =  (String)src;     
        String[] msg = body.split(",");     
        String email = msg[0];     
        String content = msg[1];     
            
        RequestContext.getEventContext().getMessage().setProperty(     
                      MailProperties.TO_ADDRESSES_PROPERTY, email);     
        System.out.println("Sent email to " + email +  " ,content: " + content);     
        return content;     
    }     
}

自定义smtp连接器(smtp connector):

public class SmtpConnector extends org.mule.transport.email.SmtpsConnector     
{     
    @Override    
    protected void extendPropertiesForSession(Properties global, Properties local, URLName url) {     
        super.extendPropertiesForSession(global, local, url);     
    
        local.setProperty("mail.smtp.starttls.enable", "true");     
        local.setProperty("mail.smtp.auth", "true");     
        local.setProperty("mail.smtps.starttls.enable", "true");     
        local.setProperty("mail.smtps.auth", "true");     
    }     
}

运行此程序, 根据提示, 在命令行输入:

Please enter email content(email address, contents):     
lichunlei@mycompt.com.cn, I come from Wuhan city!

4.2 升级:增加一个component

修改UMO:EmailBridge配置, 增加一个component:

<service name="EmailBridge">    
    <inbound>    
        <vm:inbound-endpoint path="content" />    
    </inbound>    
    <component class="co.mule.mail.EmailComponent"/>    
    <outbound>    
        <pass-through-router>    
            <smtps:outbound-endpoint user="lcllcl987"    
                password="yourpassword" host="smtp.gmail.com"    
                transformer-refs="emailModelToString StringToMimeMessage"    
                connector-ref="emailConnector" from="*@mycomp.com.cn"    
                subject="test for mule email bridge!" />    
        </pass-through-router>    
    </outbound>    
</service>

注意到增加了一个component, 接受命令行的输入(String), 产生一个EmailModel的对象之后,这个EmailModel对象进入outbound, 并经过emailModelToString, StringToMimeMessag的处理, 最后发送出去.
其中emailModelToString是新添加的一个自定义transformer:

<custom-transformer name="emailModelToString"    
    class="co.mule.mail.EmailModelToString" />

相关class如下:

EmailModel.java

package co.mule.mail;     
public class EmailModel     
{     
    private String address;     
    private String content;     
        
    public EmailModel(String address, String content)     
    {     
        this.address = address;     
        this.content = content;     
    }     
    public String getAddress()     
    {     
        return address;     
    }     
    public void setAddress(String address)     
    {     
        this.address = address;     
    }     
    public String getContent()     
    {     
        return content;     
    }     
    public void setContent(String content)     
    {     
        this.content = content;     
    }     
    @Override    
    public String toString()     
    {     
        // TODO Auto-generated method stub     
        return "address=" + address + ", content=" + content;     
    }     
}

需要说明的是:
    mule默认采用方法参数类型匹配策略, 所以, 如果有String类型的输入, foo方法自动调用, 也可以详细指定调用哪个方法,比如以下配置明确指定调用component的foo方法:

<component class="co.mule.mail.EmailComponent">    
    <method-entry-point-resolver>    
        <include-entry-point method="foo"/>    
    </method-entry-point-resolver>    
</component>

EmailComponent.java

package co.mule.mail;     
    
import org.mule.RequestContext;     
import org.mule.transport.email.MailProperties;     
    
public class EmailComponent     
{     
    public Object foo(String input)     
    {     
        String[] msg = input.split(",");     
        String address = msg[0];     
        String content = msg[1];     
        EmailModel email = new EmailModel(address, content);     
        System.out.println("create email model: " + email);     
        RequestContext.getEventContext().getMessage().setProperty(     
                MailProperties.TO_ADDRESSES_PROPERTY, email.getAddress());     
        return new EmailModel(address, content);     
    }     
}

EmailModelToString.java

package co.mule.mail;     
    
import org.mule.api.transformer.TransformerException;     
import org.mule.transformer.AbstractTransformer;     
    
public class EmailModelToString extends AbstractTransformer     
{     
    public EmailModelToString()     
    {     
        super();     
        this.registerSourceType(EmailModel.class);     
        this.setReturnClass(String.class);     
    }     
        
    @Override    
    protected Object doTransform(Object src, String encoding)     
            throws TransformerException {     
        EmailModel emailModel = (EmailModel)src;     
        return emailModel.toString();     
    }     
    
}

4.3 继续升级:不满足于命令行输入,需要在浏览器输入,也就是发布一个http接口
修改contentUMO如下:

<service name="contentUMO">    
    <!-- any number of endpoints can be added to an inbound router -->    
    <inbound>    
        <!-- Incoming HTTP requests -->    
        <inbound-endpoint address="http://localhost:9999"    
            transformer-refs="HttpRequestToString"    
            synchronous="true" />    
    </inbound>    
    <outbound>    
        <pass-through-router>    
            <vm:outbound-endpoint path="content" />    
        </pass-through-router>    
    </outbound>    
</service>

过http请求得到输入参数, 经过HttpRequestToString的转换, 放入“content” queue, 为了和content中的数据格式匹配,在浏览器中按如下方式输入:
        http://localhost:9999?email=lichunlei@mycompt.com.cn,hello
新增了一个class:
HttpRequestToString.java

package co.mule.mail;     
    
import org.mule.api.transformer.TransformerException;     
import org.mule.transformer.AbstractTransformer;     
import org.mule.util.IOUtils;     
    
import java.io.InputStream;     
import java.io.UnsupportedEncodingException;     
import java.net.URLDecoder;     
    
public class HttpRequestToString extends AbstractTransformer     
{     
    private static final String EMAIL_REQUEST_PARAMETER = "email=";     
        
    public HttpRequestToString()     
    {     
        super();     
        this.registerSourceType(String.class);     
        this.setReturnClass(String.class);     
    }     
    
    public Object doTransform(Object src, String encoding) throws TransformerException     
    {     
        return extractEmailValue(extractRequestQuery(convertRequestToString(src, encoding)));     
    }     
        
    private String convertRequestToString(Object src, String encoding)     
    {     
    
        return src.toString();     
    }     
        
    private String extractRequestQuery(String request)     
    {     
        String requestQuery = null;     
            
        if (request != null && request.length() > 0 && request.indexOf('?') != -1)     
        {     
            requestQuery = request.substring(request.indexOf('?') + 1).trim();     
        }     
    
        return requestQuery;     
    }     
        
    private String extractEmailValue(String requestQuery) throws TransformerException     
    {     
        String emailValue = null;     
            
        if (requestQuery != null && requestQuery.length() > 0)     
        {     
            int nameParameterPos = requestQuery.indexOf(EMAIL_REQUEST_PARAMETER);     
            if (nameParameterPos != -1)     
            {     
                int nextParameterValuePos = requestQuery.indexOf('&');     
                if (nextParameterValuePos == -1 || nextParameterValuePos < nameParameterPos)     
                {     
                    nextParameterValuePos = requestQuery.length();     
                }     
    
                emailValue = requestQuery.substring(nameParameterPos + EMAIL_REQUEST_PARAMETER.length(), nextParameterValuePos);     
            }     
                
            if (emailValue != null && emailValue.length() > 0)     
            {     
                try    
                {     
                    emailValue = URLDecoder.decode(emailValue, "UTF-8");     
                }     
                catch (UnsupportedEncodingException uee)     
                {     
                    logger.error(uee.getMessage());     
                }     
            }     
        }     
    
        if (emailValue == null)     
        {     
            emailValue = "";     
        }     
            
        return emailValue;     
    }     
}

继续在mule的xml汪洋中遨游。
向一个vm:queue发送map消息, mule根据map信息, 动态执行sql, 并返回数据.select 的查询mule默认返回map数据.

<?xml version="1.0" encoding="UTF-8"?>  
<mule xmlns="http://www.mulesource.org/schema/mule/core/2.1"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:jdbc="http://www.mulesource.com/schema/mule/jdbc/2.1"  
    xmlns:spring="http://www.springframework.org/schema/beans"  
    xmlns:vm="http://www.mulesource.org/schema/mule/vm/2.1"  
    xsi:schemaLocation="  
          http://www.mulesource.com/schema/mule/jdbc/2.1 http://www.mulesource.com/schema/mule/jdbc/2.1/mule-jdbc-ee.xsd  
          http://www.mulesource.org/schema/mule/core/2.1 http://www.mulesource.org/schema/mule/core/2.1/mule.xsd  
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
             http://www.mulesource.org/schema/mule/vm/2.1 http://www.mulesource.org/schema/mule/vm/2.1/mule-vm.xsd">  
    <spring:bean id="dataSource"  
        class="org.apache.commons.dbcp.BasicDataSource"  
        destroy-method="close">  
        <spring:property name="driverClassName"  
            value="com.mysql.jdbc.Driver" />  
        <spring:property name="url"  
            value="jdbc:mysql://192.168.10.120/sand_res" />  
        <spring:property name="username" value="username" />  
        <spring:property name="password" value="888" />  
        <spring:property name="maxActive" value="30" />  
        <spring:property name="maxIdle" value="10" />  
        <spring:property name="maxWait" value="1000" />  
        <spring:property name="defaultAutoCommit" value="true" />  
    </spring:bean>  
    <jdbc:connector name="jdbcConnector" dataSource-ref="dataSource">  
        <jdbc:query key="selectUser"  
            value="SELECT first_name,last_name FROM app_user where first_name=#[map-payload:firstName]" />  
        <jdbc:query key="insertUser"  
            value="insert into app_user  
            (id,first_name,last_name ) values(#[map-payload:id], #[map-payload:firstName], #[map-payload:lastName])" />  
    </jdbc:connector>  
     
    <!-- 
        The Mule model initialises and manages your UMO components 
    -->  
    <model name="databaseModel">  
        <service name="insertUMO">  
            <!-- any number of endpoints can be added to an inbound router -->  
            <inbound>  
                <vm:inbound-endpoint path="query"/>  
            </inbound>  
            <!--  
                An outbound router can have one or more router configurations that can be  
                invoked depending on business rules, message contents, headers or any other  
                criteria. The pass-through-router is a router that automatically passes  
                on every message it receives  
            -->  
            <outbound>  
                <pass-through-router>  
                    <jdbc:outbound-endpoint queryKey="selectUser" synchronous="true"/>  
                </pass-through-router>  
            </outbound>  
        </service>  
    </model>  
</mule>

注意: 如果mule采用2.1, jdbc transport的namespase后缀为com, 而不是org, 如果写错,IDE不会提示,程序异常也很奇怪,让我折腾了一个下午:(
测试程序:

public class MyMuleClientTest  
{  
    public static void main(String[] args) throws MuleException  
    {  
        // create mule  
        MuleContext muleContext;  
        String config = "my-mule-jdbc-config.xml";  
        muleContext = new DefaultMuleContextFactory().createMuleContext(config);  
        muleContext.start();  
        // create mule client  
        MuleClient client = new MuleClient();  
        Map map = new HashMap();  
        map.put("firstName", "feng");  
        MuleMessage response = client.send("vm://query", map, null);         
        System.out.println("response = " + response.getPayload());  
    }  
}

执行的sql为:

SELECT first_name,last_name FROM app_user where first_name="feng"

insert的执行类似,只需修改如下:

<outbound>  
    <pass-through-router>  
        <jdbc:outbound-endpoint queryKey="insertUser" synchronous="true"/>  
    </pass-through-router>  
</outbound>

mule的jdbc transport功能过于简单, 今天的需求是把ibatis集成进来, 作为一个service的component, 以增强持久层功能.
mule可以直接引用spring的配置文件, 方法如下:

<spring:beans>  
    <spring:import resource="applicationContext.xml" />  
    <spring:import resource="applicationContext-ibatis.xml" />  
</spring:beans>

作为一个演示,我需要往一个vm:queue中写入消息,component(由spring bean充当)
得到消息, 并作为查询参数 从数据库查询数据并返回.
 model定义如下:

<model name="databaseModel">  
<service name="databaseUMO">  
    <!-- any number of endpoints can be added to an inbound router -->  
    <inbound>  
        <vm:inbound-endpoint path="query" />  
    </inbound>  
    <component>  
        <method-entry-point-resolver>  
            <include-entry-point method="getUser" />  
        </method-entry-point-resolver>  
        <spring-object bean="userDao"></spring-object>  
    </component>  
</service>

mule中关于component的xsd很不友好, component的子项居然是一个序列, 次序不能颠倒.
现在的任务就是完成userDao 的构建.

首先给出dao的接口:

public interface Dao {  
  
public Object save(String sqlId, Object parameterObject);  
public int delete(String sqlId, Object parameterObject);  
public int update(String sqlId, Object parameterObject);  
public List query(String sqlId, Object parameterObject);  
public Object queryObject(String sqlId, Object parameterObject);  
public Connection getConn();

实现方法:

Public interface UserDao extends Dao {  
    public List getUsers ();  
    public User getUser (Long userId);  
    public void saveUser (User user);  
    public void removeUser(Long userId);
}

public class UserDaoiBatis extends BaseDao implements UserDao {  
    private DataFieldMaxValueIncrementer incrementer;  
     
    public void setIncrementer(DataFieldMaxValueIncrementer incrementer) {  
        this.incrementer = incrementer;  
    }  
    public List getUsers() {  
        return getSqlMapClientTemplate().queryForList("getUsers", null);  
    }  
    public User getUser(Long id) {  
        User user =  
            (User) getSqlMapClientTemplate().queryForObject("getUser", id);  
        if (user == null) {  
            throw new ObjectRetrievalFailureException(User.class, id);  
        }  
        return user;  
    }  
    public void saveUser(User user) {  
        if (user.getId() == null) {  
            Long id = new Long(incrementer.nextLongValue());  
            user.setId(id);  
            // To use iBatis's <selectKey> feature, which is db-specific, comment  
            // out the above two lines and use the line below instead  
             
            // Long id = (Long) getSqlMapClientTemplate().insert("addUser", user);  
            getSqlMapClientTemplate().insert("addUser", user);  
            logger.info("new User id set to: " + id);  
        } else {  
            getSqlMapClientTemplate().update("updateUser", user);  
        }  
    }  
    public void removeUser(Long id)  {  
        getSqlMapClientTemplate().update("deleteUser", id);  
    }  
}

在spring配置文件中, 装配userDao:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
        <property name="driverClassName" value="${jdbc.driverClassName}"/>  
        <property name="url" value="${jdbc.url}"/>  
        <property name="username" value="${jdbc.username}"/>  
        <property name="password" value="${jdbc.password}"/>  
        <property name="maxActive" value="30"/>  
        <property name="maxIdle" value="10"/>  
        <property name="maxWait" value="1000"/>  
        <property name="defaultAutoCommit" value="true"/>  
    </bean>  
     
    <!-- Transaction manager for iBATIS DAOs -->  
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource"/>  
    </bean>  
     
    <!-- SqlMap setup for iBATIS Database Layer -->  
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
        <property name="dataSource" ref="dataSource"/>  
        <property name="configLocation" value="classpath:/co/iplatform/dao/sql-map-config.xml"/>  
    </bean>  
    <bean id="userIncrementer" class="org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer">  
        <property name="dataSource" ref="dataSource"/>  
        <property name="incrementerName" value="user_sequence"/>  
        <property name="columnName" value="value"/>  
    </bean>  
     
    <bean id="userDao" class="co.iplatform.dao.UserDaoiBatis">  
        <property name="incrementer" ref="userIncrementer"/>  
        <property name="sqlMapClient" ref="sqlMapClient"/>  
    </bean>  
    <!-- Add additional DAO definitions here -->  
</beans>

ibatis的配置文件标准,就不贴了。
写一个userDao的测试, 以确保dao正常工作:

public abstract class BaseDaoTestCase extends AbstractTransactionalDataSourceSpringContextTests {  
    protected final Log log = logger;  
    private ApplicationContext ctx;  
    protected String[] getConfigLocations() {  
        setAutowireMode(AUTOWIRE_BY_NAME);  
        String[] paths = {"classpath*: applicationContext*.xml" };  
        return paths;  
    }  
}  
public class UserDaoTest extends BaseDaoTestCase {  
    private User user = null;  
    private UserDao dao = null;  
    public void setUserDao(UserDao userDao) {  
        this.dao = userDao;  
    }  
    public void testGetUsers() {  
        user = new User();  
        user.setFirstName("li");  
        user.setLastName("chunlei");  
        dao.saveUser(user);  
        System.out.println("size--"+dao.getUsers().size());  
        assertTrue(dao.getUsers().size() >= 1);  
    }  
    public void testSaveUser() throws Exception {  
        user = new User();  
        user.setFirstName("li");  
        user.setLastName("chunlei");  
        dao.saveUser(user);  
        assertTrue("primary key assigned", user.getId() != null);  
        assertNotNull(user.getFirstName());  
    }  
    public void testAddAndRemoveUser() throws Exception {  
        user = new User();  
        user.setFirstName("feng");  
        user.setLastName("Joy");  
        dao.saveUser(user);  
        assertNotNull(user.getId());  
        assertTrue(user.getFirstName().equals("feng"));  
        log.debug("removing user");  
        dao.removeUser(user.getId());  
        endTransaction();  
        try {  
            user = dao.getUser(user.getId());  
            fail("User found in database");  
        } catch (DataAccessException dae) {  
            log.debug("Expected exception: " + dae.getMessage());  
            assertNotNull(dae);  
        }  
    }  
}

MyMuleClientTest.java

public class MyMuleClientTest  
{  
    public static void main(String[] args) throws MuleException  
    {  
        // create mule  
        MuleContext muleContext;  
        String config = "mule-database-config.xml";  
        muleContext = new DefaultMuleContextFactory().createMuleContext(config);  
        muleContext.start();  
        // creat mule client  
        MuleClient client = new MuleClient();  
        MuleMessage response = client.send("vm://query", new Long(11), null);         
        System.out.println("response = " + response.getPayload());  
    }  
}

4.4 消息传递方式

4.4.1 异步方式

异步方式是一种单向调用,调用者不需要获得响应。

如果只想将消息以“即发即弃(fire andforget)”的方式发送给一个服务,(并不需要给调用者返回响应),那么可使用异步消息类型。如果将入站端点的synchronous属性设置为false,它就不会给调用者返回响应。

<model name="Asynchronous_Message_Pattern"> 
    <service name="AsynchronousService">   
     <inbound>      
        <jms:inbound-endpoint queue="test.in" synchronous="false"/>
    </inbound>    
    <component class="org.myorg.WidgetHandler"/>   
    <outbound>     
       <pass-through-router>      
          <jms:outbound-endpoint queue="test.out">   
       </pass-through-router>   
    </outbound> 
    </service>
</model>

4.4.2 请求-响应方式

请求-响应方式即请求方调用服务后,服务立即处理并返回响应结果,不需将消息再次传递。

在简单的Request-Response场景中,服务在一个同步的入口端点上接收请求,并处理该请求,然后将它作为回复发送给调用者。例如,如果用户在HTML表单中输入一个值,想转换该值并将其结果显示在同一个页面上,那么可以在该服务上简单地配置一个同步入站端点,由该服务完成数据转换。这种场景并不需要使用出站端点。这就是request-response消息类型。

<model name="Request-Response_Message_Pattern"> 
   <service name="SynchronousService">   
   <!-- 为了返回response将synchronous的值设置为“true”-->    
   <inbound>     
       <http:inbound-endpoint host="localhost" port="8080"   path="/mule/services" synchronous="true"/>    
   </inbound>   
   <!-- 指定处理该请求的组件 -->   
   <component class="org.myorg.WidgetHandler"/>  
   </service>
 </model>

4.4.3 同步

如果为了进一步处理消息,需要将消息传递给第二个服务,那么需要在第一个服务上配置一个出站路由器将该消息传递给第二个服务。在第二个服务处理完消息后,第一个服务将它作为回复发送给调用者。值得注意的是将第一个服务设置为同步入口端点就意味着之后的所有服务都会以同步的方式处理该消息,所以无需在第二个服务上设置synchronous属性的值。这就是同步消息类型。

<model name="Synchronous_Message_Pattern">  
   <service name="SynchronousService">    
       <inbound>     
        <!-- 为了返回response将synchronous的值设置为“true” --> 
    <jms:inbound-endpoint queue="test.in" synchronous="true"/>   
       </inbound>  
       <component class="org.myorg.WidgetHandler"/>   
       <outbound>      
       <!-- 使用pass-through路由器时,如果想返回response必须将synchronous的值设置为“true”-->     
            <pass-through-router>      
        <!-- 设置出站端点 -->        
          <jms:outbound-endpoint queue="test.out" synchronous="true"/>  
        </pass-through-router>    
    </outbound>  
    </service> 
    <!-- 配置第二个服务,并将它的入站端点设置为上一个服务的出站端点的路径。   值得注意的是无需设置synchronous的值,因为在第一个服务中已经将消息设置为synchronous了。  --> 
    <service>   
       <inbound>      
          <jms:inbound-endpoint queue="test.out"/>   
       </inbound>  
       <component class="org.myorg.WidgetProcesser"/>
    </service>
  </model>

4.4.4 异步请求-响应方式

异步请求-响应方式即请求方调用服务后不需要立即获得返回结果,component将请求发送给其他外围系统处理(可能有多个),全部处理完毕后通过指定的异步应答Router返回给请求方。

在大多数复杂的场景中,可以使用request-response消息,并使用后端(back-end)流程调用其它的服务,并基于多个服务调用的结果异步地返回一个回复。你可以将入站端点的synchronous属性设置为false,因为异步回复路由器会处理该回复,除非你想给调用者发送响应。这就是异步request-response消息类型。

在下面的例子中,HTTP端点接收一个请求,并使用Multicast路由器将该请求广播到两个端点,再将这些结果以异步的方式发送到一个JMS端点。

<model name="Async_Request-Response_Message_Pattern"> 
    <service name="AsyncRequestResponseService">    
       <inbound>      
           <!-- 将synchronous设置为“false”,因为response将由异步回复路由器处理  -->      
        <http:inbound-endpoint host="localhost" port="8080"   path="/mule/services" 
        synchronoussynchronous="false"/>   
      </inbound>    
      <component class="org.myorg.WidgetHandler"/>  
      <!-- 配置异步回复的设置。这个例子使用了收集异步回复路由器,        在发送回复信息之前,它将所有的响应信息收集在一起。 -->   
      <async-reply timeout="5000>     
          <collection-async-reply-router/>     
      <jms:inbound-endpoint queue="reply.queue"/>   
      </async-reply>    
      <!--设置负责接收和处理消息的端点以及回复消息的端点    -->   
      <outbound>     
          <multicasting-router>       
           <reply-to address="jms://reply.queue"/>      
           <jms:outbound-endpoint queue="service1" synchronous="false"/>    
           <jms:outbound-endpoint queue="service2" synchronous="false"/>      
        </multicasting-router>  
      </outbound> 
    </service>
</model>

4.5 将消息传递到另一个端点

pass-through路由器是为简化端点间的消息传递而设计的。比如,它对分发消息给一个队列非常有用。

也可以使用pass-through路由器将协议桥接到其它的出站端点。例如:

<service name="HttpProxyService"> 
   <inbound>    
      <inbound-endpoint address="http://localhost:8888" synchronous="true"/>  
   </inbound> 
   <outbound>  
      <pass-through-router>     
         <outbound-endpoint address="http://www.webservicex.net#[header:http.request]"    
     synchronous="true"/>   
      </pass-through-router> 
   </outbound>
 </service>

当使用pass-through路由器时,如果想返回一个响应,必须将出站端点的synchronous属性设置为true。其它的路由器,比如chaining路由器并不需将出站端点的synchronous属性设置为true,该路由器总会在同步的场景中返回一个响应。因此,如果将消费发送给多个服务,可能会用chaining路由器代替pass-through路由器,因为chaining路由器中不需要将每个端点的synchronous设置为true。

4.6 过滤消息

使用过滤器可以控制服务处理哪些消息。选择性消费者路由器(Selective ConsumerRouter)用于入站端点,它可以控制服务处理哪些消息。过滤路由器(FilteringRouter)用于出站端点,可以控制哪些消息发送到下一个服务上。可以组合使用这些过滤器来控制消息流。

例如,如果只想处理不包含错误的消息,那么可以使用选择性消费者以确保只处理结果代码为success的消息。并使用Catch-all策略将其它的消息转发到另外端点上作为错误处理:

<inbound>  
   <selective-consumer-router>  
      <mulexml:jxpath-filter expression="msg/header/resultcode = 'success'"/>  
   </selective-consumer-router> 
   <forwarding-catch-all-strategy>   
       <jms:endpoint topic="error.topic"/> 
   </forwarding-catch-all-strategy>
</inbound>

服务处理消息时,如果想通过指定的标准决定将消息发送到哪个端点,那么可以在出站端点上使用过滤路由器。在下面的示例中,将包含异常信息的消息发送到系统管理员的email邮箱,将包含特定字符串的消息发送到名为string.queue的队列,并使用forwardingcatch-all路由器接收余下的所有消息,并将它们发送到名为error.queue的死信队列:

<outbound> 
 <filtering-router>   
    <smtp:outbound-endpoint to="ross@muleumo.org"/>     
    <payload-type-filter expectedTypeexpectedType="java.lang.Exception"/> 
 </filtering-router> 
 <filtering-router>  
     <jms:outbound-endpoint to="string.queue"/>    
 <and-filter>     
       <payload-type-filter expectedType="java.lang.String"/>      
       <regex-filter pattern="the quick brown (.*)"/>   
 </and-filter> 
 </filtering-router>  
 <forwarding-catch-all-strategy>  
    <jms:outbound-endpoint queue="error.queue"/> 
    </forwarding-catch-all-strategy>
</outbound>

与过滤路由器(filtering router)相似的路由器有转发路由器(forwardingrouter),它可以处理一些消息并可以选择性地将消息转发到其它路由器,还有wiretaprouter,这种路由器可以处理所有的消息,并将它们发送到端点上,同时也将消息的副本发送到另外一个端点。

4.7 将多个出站端点链接在一起

假设我们有一个验证服务,当消息没有通过验证时,想将该消息以及验证异常转发到另一个服务,并将消息和验证异常返回给调用者。那么可以使用链接路由器(chainingrouter),它是一个高速的、轻量级的可配置路由器,可用于将消息发送到端点,然后将该端点的输出结果发送到另一个端点。例如:

<chaining-router> 
   <!-- 首先,将消息发送到这个端点,用于验证。 -->
    <vm:outbound-endpoint path="ValidationService" synchronous="true"/> 
    <!-- 接着将包含表达式的消息发送到这个端点上 -->  
    <vm:outbound-endpoint path="ValidationError" synchronous="true">   
         <exception-type-filter expectedType="java.lang.Exception"/>  
    </vm:outbound-endpoint>
 </chaining-router>

4.8 消息分解

消息分解器(message splitter)可用于将输出消息(outgoingmessage)分解成多个部分,再将他们分发到配置在路由器(router)上的不同端点。例如,在订单处理应用中,如果想将经消息分解后的不同部分分发给不同的服务去处理,那么可以使用下面的路由器:

列表消息分解器(List MessageSplitter):接收一个对象列表,这些对象将被路由到不同的端点。例如:

<outbound> 
    <list-message-splitter-router">   
        <!-- 将order路由到队列order. Queue -->  
        <jms:outbound-endpoint queue="order.queue">   
        <payload-type-filter expectedType="com.foo.Order"/>   
           </jms:outbound-endpoint>  
           <!-- 将items路由到队列item.queue -->   
          <jms:outbound-endpoint queue="item.queue">    
        <payload-type-filter expectedType="com.foo.Item"/>    
         </jms:outbound-endpoint> 
   </list-message-splitter-router>
</outbound>

表达式分解路由器(Expression
SplitterRouter):它与列表消息分解器相似,只是它是基于表达式分解消息,将消息分解成一个或者多个部分。例如:

<outbound>  
   <expression-splitter-router  evaluator="xpath" expression="/mule:mule/mule:model/mule:service"   disableRoundRobin="true"          failIfNoMatch="false">   
   <outbound-endpoint ref="service1">      
        <expression-filter   evaluator="xpath"   expression="/mule:service/@name = 'service splitter'"/> 
    </outbound-endpoint>   
    <outbound-endpoint ref="service2">      
    <expression-filter evaluator="xpath" expression="/mule:service/@name = 'round robin deterministic'"/>    </outbound-endpoint> 
  </expression-splitter-router>
</outbound>

为了提高性能也可以将消息分解成多个部分。轮叫(RoundRobin)消息分解器将消息分解成多个部分,并以轮叫(round-robin)的方式将它们发送到端点。Message ChunkingRouter将消息按固定长度分解成多个部分,并将它们路由到同一个端点。

消息分解之后,可以使用Message ChunkingAggregator重新将消息块聚合在一起。该聚合器(aggregator)通过关联ID(correlationID)来识别哪些消息块属于同一个消息,关联ID(correlation ID)在出站路由器(outbound router)上设置。

<inbound> 
   <message-chunking-aggregator-router>    
       <expression-message-info-mapping    correlationIdExpression="#[header:correlation]"/>  
       <payload-type-filter expectedType="org.foo.some.Object"/>  
   </message-chunking-aggregator-router>
</inbound>

4.9 处理消息仅一次

幂等接收器(IdempotentReceiver)通过核对输入消息的唯一消息ID来保证只有拥有唯一ID的消息才能被服务所接收。消息ID可以通过使用一个表达式从消息中产生,该表达式在idExpression属性中定义。#[message:id]是默认的表达式,也就是说如果要实现该功能,端点必须支持唯一性消息ID。在下面的例子中,唯一性ID是由消息ID和消息标头中标签的内容组合而成。所有的消息ID都被记录到一个简单的文本文件中,用于追踪哪些消息已经处理过。

<inbound> 
    <idempotent-receiver-router idExpression="#[message:id]-#[header:label]">  
    <simple-text-file-store directory="./idempotent"/> 
    </idempotent-receiver-router>
 </inbound>

4.10 通过组件绑定调用外部服务

除了使用消息路由器控制服务间的消息流之外,也可以通过组件绑定(Component
Bindings)调用处理消息的外部服务(ExternalService)。

在这个方法中,可以将Mule的端点绑定到Java接口方法。该方法的优势在于,在组件仍在处理消息时,你可以使用外部服务,而无需使用Mule的API或者修改组件的代码。相反,只需要在XML配置文件中配置组件绑定,从而指定外部服务的端点。例如,在下面的绑定例子中,当sayHello方法被调用时,HelloInterface中的sayHello方法会调用外部的HelloWeb服务。

<component class="org.mule.examples.bindings.InvokerComponent">   
    <binding interface="org.mule.examples.bindings.HelloInterface"        method="sayHello"> 
    <cxf:outbound-endpoint        address="http://myhost.com:81/services/HelloWeb?method=helloMethod"          synchronous="true"/>   
  </binding>
</component>

5. Mule ESB Hello实例

这是一个最简单的mule esb入门实例,其中用了flow ,endpoint ,transformer,component 这些组件,这些组件的配置也是最简单配置.

情景:

1.在浏览器地址栏输入http://localhost:9090/hello-mule?name=oliver&age=20,会返回"Hello oliver,your age is 10".

2.在浏览器地址栏输入http://localhost:9090/love-mule?name=oliver&age=20,会返回"Love oliver,your age is 10".

5.1 配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <mule xmlns="http://www.mulesoft.org/schema/mule/core"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:spring="http://www.springframework.org/schema/beans"
  5. xmlns:http="http://www.mulesoft.org/schema/mule/http"
  6. xmlns:vm="http://www.mulesoft.org/schema/mule/vm"
  7. xsi:schemaLocation="
  8. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  9. http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.2/mule.xsd
  10. http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/3.2/mule-http.xsd
  11. http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/3.2/mule-vm.xsd">
  12. <!-- 将客户端传过来的Http数据转化成Person对象 -->
  13. <custom-transformer name="HttpDataToPersonTransformer" class="com.mule.transformer.HttpDataToPersonTransformer"/>
  14. <!-- 将service操作结果用Html语言封装起来 -->
  15. <custom-transformer name="StringToHtmlTransformer" class="com.mule.transformer.StringToHtmlTransformer"/>
  16. <!-- 设置相应内容为text/html格式 -->
  17. <message-properties-transformer name="setHtmlContentType" overwrite="true">
  18. <add-message-property key="Content-Type" value="text/html" />
  19. <!-- Tomcat lowercases headers, need to drop this old one too -->
  20. <delete-message-property key="content-type" />
  1. </message-properties-transformer>
  2. <!-- 根据客户端传过来的内容,返回相应的Hello语句 -->
  3. <flow name="hello">
  4. <inbound-endpoint address="http://localhost:9090/hello-mule?method=sayHello" exchange-pattern="request-response" >
  5. <transformer ref="HttpDataToPersonTransformer"/>
  6. <response>
  7. <transformer ref="StringToHtmlTransformer"/>
  8. <transformer ref="setHtmlContentType"/>
  9. </response>
  10. </inbound-endpoint>
  11. <component class="com.mule.component.WelcomService"/>
  12. </flow>
  13. <!—根据客户端传过来的内容,返回相应的Love语句 -->
  14. <flow name="love">
  15. <inbound-endpoint address="http://localhost:9090/love-mule?method=sayLove" exchange-pattern="request-response" >
  16. <transformer ref="HttpDataToPersonTransformer"/>
  17. <response>
  18. <transformer ref="StringToHtmlTransformer"/>
  19. <transformer ref="setHtmlContentType"/>
  20. </response>
  21. </inbound-endpoint>
  22. <component class="com.mule.component.WelcomService"/>
  23. </flow>
  24. </mule>

5.2 Transformer

1.HttpDataToperson 将客户端传过来的http数据转换成component所需要Person类型

  1. package com.mule.transformer;
  2. import org.mule.api.MuleMessage;
  3. import org.mule.api.transformer.TransformerException;
  4. import org.mule.transformer.AbstractMessageTransformer;
  5. import com.mule.domain.Person;
  6. public class HttpDataToPersonTransformer extends AbstractMessageTransformer {
  7. @Override
  8. public Object transformMessage(MuleMessage message, String outputEncoding)
  9. throws TransformerException {
  10. String name = message.getInboundProperty("name");
  11. String age = message.getInboundProperty("age");
  12. if( age == null ) {
  13. age = "10";
  14. }
  15. return new Person(name,Integer.parseInt(age));
  16. }
  17. }

2.StringToHtmlTransformer
将component处理后返回的string类型数据,封装成html数据

  1. package com.mule.transformer;
  2. import org.mule.api.transformer.TransformerException;
  3. import org.mule.transformer.AbstractTransformer;
  4. import com.mule.util.HtmlTemplateUtil;
  5. public class StringToHtmlTransformer extends AbstractTransformer {
  6. @Override
  7. protected Object doTransform(Object src, String enc)
  8. throws TransformerException {
  9. String str = (String)src;
  10. return HtmlTemplateUtil.getSimpleHtml(str);
  11. }
  12. }

5.3 Component

WelcomService 返回欢迎内容

  1. package com.mule.component;
  2. import com.mule.domain.Person;
  3. public class WelcomService {
  4. public String sayHello( Person person ){
  5. return " Hello "+person.getName()+", your age is "+person.getAge();
  6. }
  7. public String sayLove( Person person){
  8. return " Love you "+person.getName()+", your age is "+person.getAge();
  9. }
  10. }

5.4 Person类

  1. package com.mule.domain;
  2. public class Person {
  3. private String name;
  4. private int age;
  5. public Person(){}
  6. public Person(String name,int age ){
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public int getAge() {
  17. return age;
  18. }
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22. }

5.5 HtmlTemplateUtil工具类

HtmlTemplateUtil工具类:用html语句封装给定的text

  1. package com.mule.util;
  2. public class HtmlTemplateUtil {
  3. public static String getSimpleHtml( String text ){
  4. String output = "";
  5. output += "<html>";
  6. output += "<head>";
  7. output += "<meta http-equiv=\"Content-Language\" content=\"en-us\"/>";
  8. output += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\"/>";
  9. output += "<title>Bookstore Administration Console</title>";
  10. output += "</head>";
  11. output += "<body link=\"#FFFFFF\" vlink=\"#FFFFFF\" alink=\"#FFFFFF\" bgcolor=\"#990000\" text=\"#FFFFFF\">";
  12. output += text;
  13. output += "</body>";
  14. output += "<br/><a href=\"/bookstore-admin/\">Return to Home Page</a>";
  15. output += "</html>";
  16. return output;
  17. }
  18. }

6. Mule ESB webservice jms服务

6.1 服务调用

6.1.1 Mule实现并提供Web Service

在Mule上开发并发布一个Web Service供客户端调用。

  • 示例配置

<flow name="local-ws">

<core:inbound-endpoint address="http://localhost:65082/services/Echo1"

exchange-pattern="request-response" doc:name="Generic" doc:description="Generic endpoint specified by
address URI" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" doc:name="SOAP"

doc:description="Make a web service available via
CXF" />

<component doc:name="Component" doc:description="Invoke a Java component">

<singleton-object class="demo.mule.component.Echo" />

</component>

</ flow >

  • 测试方法

在浏览器地址栏中输入“http://localhost:65082/services/Echo1/echo/text/hello”,回车后浏览器中将显示返回结果信息。地址中的“echo”是服务的方法,“text”是方法的参数,“hello”是参数的值。

6.1.2 Web Service Proxy

Web Service Proxy用来将客户端的WS请求直接转发至相应的远程WS服务端处理,并返回处理结果。Mule本身不做任何处理。

6.1.2.1 配置方式1

  • 示例配置

<flow name="local2remote-ws">

<http:inbound-endpoint keep-alive="false" address="http://localhost:65000"

encoding="UTF-8" disableTransportTransformer="false" exchange-pattern="request-response"doc:name="HTTP"  doc:description="" />

<http:outbound-endpoint method="GET" keep-alive="false"

address="http://localhost:5050#[header:INBOUND:http.request]" responseTimeout="10000"encoding="UTF-8"

disableTransportTransformer="false" followRedirects="false" exchange-pattern="request-response" doc:name="HTTP" doc:description="" />

</ flow >

  • 说明

注意outbound-endpoint中address参数中的表达式。

  • 测试方法

浏览器中通过“http://localhost:65000/webservice/EchoService?wsdl”(将内容复制,保存为*.wsdl),然后使用SoapUI测试。

6.1.2.2 配置方式2

  • 示例配置

<pattern:web-service-proxy name="ws-proxy" inboundAddress="http://localhost:65082/services/Echo2"

outboundAddress="http://localhost:65082/services/Echo1?method=echo">

</pattern:web-service-proxy>

  • 说明

Mule为这种常见的场景提供了现成的模式,以简化配置。

  • 测试方法

通过“http://localhost:65082/services/Echo2?wsdl”获取wsdl文件,然后使用SoapUI测试。

6.1.3 Web Service to Web Service

Web Service To Web Service用于在Mule中提供Web Service供客户端调用,Mule接收请求后调用远端的Web Service进行处理,并返回结果。

  • 示例配置

<flow name="local-ws2remote-ws">

<core:inbound-endpoint address="http://localhost:65082/services/Echo8"

disableTransportTransformer="false" exchange-pattern="request-response" doc:name="Generic" doc:description="Generic endpoint specified by
address URI" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" doc:name="SOAP"

doc:description="Make a web service available via
CXF" />

<core:outbound-endpoint

address="wsdl-cxf:http://server1:5050/mule-business/webservice/EchoService?wsdl&amp;method=Echo" />

</ flow >

  • 说明

注意outbound-endpoint中address参数的配置方式,使用了wsdl-cxf前缀表示此web service是由cxf提供的。

  • 测试方法

在浏览器中输入“http://localhost:65082/services/Echo8/echo/text/hello”进行测试。

6.1.4 Socket to Socket

Socket To Socket用于将客户端的Socket请求转发至远程的Socket服务端处理,并返回处理结果。

  • 示例配置

<flow name="tcp2tcp">

<tcp:inbound-endpoint host="localhost" port="7100" responseTimeout="10000"

encoding="UTF-8" disableTransportTransformer="false" exchange-pattern="request-response"doc:name="TCP"

doc:description="The TCP transport enables events
to be sent and received over TCP sockets." />

<tcp:outbound-endpoint host="localhost" port="7000" responseTimeout="10000"

encoding="UTF-8" disableTransportTransformer="false" exchange-pattern="request-response"doc:name="TCP"

doc:description="The TCP transport enables events
to be sent and received over TCP sockets." />

</ flow >

  • 说明

主要配置host、port参数,表明服务地址。

  • 测试方法

通过  SimpleServer和SimpleClient测试类,首先启动SimpleServer,然后启动SimpleClient,发送请求并接收处理结果。

6.1.5 JMS Topic

客户端发送Web Service请求,Mule将请求消息发送至远程JMS的Topic中。

  • 示例配置

<flow name="local-ws2jms-topic">

<core:inbound-endpoint address="http://localhost:65082/services/Echo3"

responseTimeout="10000" encoding="UTF-8" disableTransportTransformer="false" mimeType="text/plain"

exchange-pattern="one-way" doc:name="Generic" doc:description="Generic
endpoint specified by address URI" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" doc:name="SOAP"

doc:description="Make a web
service available via CXF" />

<jms:outbound-endpoint topic="topic1" responseTimeout="10000" encoding="UTF-8"

disableTransportTransformer="false" disableTemporaryReplyToDestinations="false" exchange-pattern="one-way"

connector-ref="activemqConnector" doc:name="JMS" doc:description="Send or
receive messages from a JMS queue" />

</flow>

<flow name="jms-topic2echo">

<jms:inbound-endpoint topic="topic1" responseTimeout="10000" encoding="UTF-8"

disableTransportTransformer="false" disableTemporaryReplyToDestinations="false" exchange-pattern="one-way"

connector-ref="activemqConnector" doc:name="JMS" doc:description="Send or
receive messages from a JMS queue" />

<echo-component doc:name="Echo" doc:description="Echoes message payload." />

</flow>

  • 说明

JMS endpoint是单向的,不需要返回值。通过topic属性指定JMS Server的Topic名称,connector-ref指明了使用的JMS连接。

  • 测试方法

在浏览器地址栏中输入“http://localhost:65082/services/Echo3/echo/text/hello”发送请求,Mule控制台上输出订阅者的处理结果(上述示例中通过Mule配置了一个JMS的订阅者)。也可以通过ActiveMQ的控制台,查看到Topic中增加了一条发布的消息。

6.2 基于消息内容的路由

Mule提供基于消息内容的路由机制,根据消息中的指定信息,将消息发送至不同的服务端进行处理。

6.2.1 Socket to Socket 路由

  • 示例配置

<flow name="tcp2tcp-router">

<tcp:inbound-endpoint host="localhost" port="7101" responseTimeout="10000"

encoding="UTF-8" disableTransportTransformer="false" exchange-pattern="request-response"doc:name="TCP"

doc:description="The TCP transport enables events
to be sent and received over TCP sockets." />

<choice>

<when evaluator="jxpath" expression="(req/area)='bj'">

<tcp:outbound-endpoint host="server1" port="7101"

responseTimeout="10000" encoding="UTF-8" disableTransportTransformer="false" exchange-pattern="request-response"

doc:name="TCP" doc:description="The TCP transport enables events
to be sent and received over TCP sockets." />

</when>

<when evaluator="jxpath" expression="(req/area)='sh'">

<tcp:outbound-endpoint host="server1" port="7102"

responseTimeout="10000" encoding="UTF-8" disableTransportTransformer="false" exchange-pattern="request-response"

doc:name="TCP" doc:description="The TCP transport enables events
to be sent and received over TCP sockets." />

</when>

</choice>

</flow>

  • 说明

路由使用了<choice>、<when>元素,表示路由分支。When元素使用evaluator指明表达式的解析方式,使用expression描述消息内容的判断条件。

  • 测试方法

同Socket To Socket测试,消息内容分别为<req><area>bj</area></req>、<req><area>sh</area></req>,查看发送至不同服务器的输出。

6.2.2 Web Service to JMS Topic 路由

  • 示例配置

<flow name="local-ws2jms-topic-router">

<core:inbound-endpoint address="http://localhost:65082/services/Echo7"

disableTransportTransformer="false" exchange-pattern="request-response" doc:name="Generic"

doc:description="Generic endpoint specified by
address URI" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" doc:name="SOAP"

doc:description="Make a web service available via
CXF" />

<choice>

<when evaluator="jxpath" expression="(req/area)='bj'">

<jms:outbound-endpoint topic="topic1" responseTimeout="10000" encoding="UTF-8"

disableTransportTransformer="false" disableTemporaryReplyToDestinations="false"

exchange-pattern="one-way" connector-ref="activemqConnector" doc:name="JMS"

doc:description="Send or receive messages from a
JMS queue" />

</when>

<when evaluator="jxpath" expression="(req/area)='sh'">

<jms:outbound-endpoint topic="topic2" responseTimeout="10000" encoding="UTF-8"

disableTransportTransformer="false" disableTemporaryReplyToDestinations="false"

exchange-pattern="one-way" connector-ref="activemqConnector" doc:name="JMS"

doc:description="Send or receive messages from a
JMS queue" />

</when>

</choice>

</flow>

  • 测试方法

通过“http://localhost:65082/services/Echo7?wsdl”获取wsdl文件,然后通过SoapUI发送请求,查看返回结果。修改消息内容,查看结果的变化。

6.2.3 Web Service to Web Service 路由

  • 示例配置

<flow name="local-ws2jms-topic-router">

<core:inbound-endpoint address="http://localhost:65082/services/Echo9"

disableTransportTransformer="false" exchange-pattern="request-response" doc:name="Generic"

doc:description="Generic endpoint specified by
address URI" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" doc:name="SOAP"

doc:description="Make a web service available via
CXF" />

<choice>

<when evaluator="jxpath" expression="(req/area)='bj'">

<core:outbound-endpoint

address="wsdl-cxf:http://server1:5050/mule-business/webservice/CalcService?wsdl&amp;method=processXml" />

</when>

<when evaluator="jxpath" expression="(req/area)='sh'">

<core:outbound-endpoint

address="wsdl-cxf:http://server2:5050/mule-business/webservice/CalcService?wsdl&amp;method=processXml" />

</when></choice>

</flow>

  • 测试方法

使用“<![CDATA[<req><seq>1</seq><area>bj</area><price>123.45</price><count>10</count></req>]]>”数据进行测试。

6.3 数据转换

6.3.1 压缩解压

Mule原生提供了gzip压缩方式的Transformer。

  • 示例配置

<flow name="gzip">

<stdio:inbound-endpoint ref="stdioInEndpoint" />

<core:string-to-byte-array-transformer encoding="UTF-8" />

<component class="demo.mule.component.Passthrough" />

<core:gzip-compress-transformer encoding="UTF-8" />

<component class="demo.mule.component.Passthrough" />

<core:gzip-uncompress-transformer encoding="UTF-8" />

<component class="demo.mule.component.Passthrough" />

<core:byte-array-to-string-transformer encoding="UTF-8" />

<stdio:outbound-endpoint ref="stdioOutEndpoint" />

</flow>

  • 说明

gzip-compress-transformer针对byte[]进行压缩处理,因此对于字符串类型的消息,首先需要通过string-to-byte-array-transformer进行转换。

  • 测试方法

在控制台的提示信息后输入测试字符串,完成后控制台输出同样的信息。

6.3.2 加密解密

加密、解密是一种特定的数据转换方式,因此通过自定义Transformer的形式支持。

  • 示例配置

<flow name="encrypt">

<core:inbound-endpoint address="http://localhost:65082/services/Echo11"

responseTimeout="10000" encoding="UTF-8" disableTransportTransformer="false" mimeType="text/plain"

exchange-pattern="one-way" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" />

<component>

<singleton-object class="demo.mule.component.Echo" />

</component>

<core:custom-transformer class="demo.mule.transformer.DesEncryptTransformer" encoding="UTF-8" />

<component class="demo.mule.component.Passthrough" />

<core:custom-transformer class="demo.mule.transformer.DesDecryptTransformer" encoding="UTF-8" />

<stdio:outbound-endpoint ref="stdioOutEndpoint" />

</flow>

  • 说明

DesEncryptTransformer是自定义的压缩转换器,DesDecryptTransformer是对应的解压转换器。

  • 测试方法

在浏览器地址栏中输入“http://localhost:65082/services/Echo11/echo/text/测试字符串”,在控制台中可见加密后的字符串和最终解密后与原串相同的字符串。

6.3.3 自定义Transformer

  • 示例代码

import demo.mule.dto.Envelope;

publicclassString2EnvelopeTransformerextendsAbstractTransformer{

privatestaticLoglog=LogFactory.getLog(String2EnvelopeTransformer.class);

@Override

protectedObjectdoTransform(Objectsrc,Stringenc)throws TransformerException{

Envelopeenv=newEnvelope();

if(srcinstanceofString){

StringTokenizerst=newStringTokenizer((String)src,",");

Stringarea=st.nextToken();

Stringdata=st.nextToken();

env.create(area,data);

}

log.debug(env);

returnenv;

}

}

  • 说明

自定义Transformer需要继承AbstractTransformer类,实现其doTransform方法。该方法接收两个参数,一个是消息对象,一个是消息的Encoding,方法抛出TransformerException,返回转换后的消息对象。

  • 实例配置

<flow name="local-ws">

<core:inbound-endpoint address="http://localhost:65082/services/Echo1"

exchange-pattern="request-response" doc:name="Generic" doc:description="Generic endpoint specified by
address URI" />

<cxf:jaxws-service serviceClass="demo.mule.component.Echo" doc:name="SOAP"

doc:description="Make a web service available via
CXF" />

<component doc:name="Component" doc:description="Invoke a Java component">

<singleton-object class="demo.mule.component.Echo" />

</component>

<core:custom-transformer class="demo.mule.transformer.String2EnvelopeTransformer"></core:custom-transformer>

<core:outbound-endpoint address="stdio://System.out" exchange-pattern="one-way" />

</flow>

  • 测试方法

在浏览器中输入“http://localhost:65082/services/Echo1/echo/text/bj,hello”进行测试,观察控制台输出结果。

7. Mule官方案例

7.1 编译导入Mule自带的例子

7.1.1 准备

安装Mule。这里就不介绍mule的安装了,请参考《Mule安装部署手册》。

7.1.2 编译Mule自带例子中的Hello例子

使用命令行到目录:D:/app/mule3.1.1/examples/hello下,输入:mvn 即可。这里需要安装Maven;

7.1.3 导入Eclipse工程

输入命令:mvn
eclipse:eclipse 成功后,即可导入eclipse。

7.1.4 导入这个工程到eclipse

(1)选择:File
> Import

(2)选择:展开“General”
选择“Existing
Projects into Workspace”

(3)选择Browse,导入后,选择Finish。完成。

7.1.5 配置Elipse工程(build
path)

(1)右击工程,选择“Build
Path ->Configure Build”:

(2)选择“Libraries->Add
Library”

(3)点击“User
Library”,点击“Next”

(4)点击“User
Libraries”,点击“New”

(5)输入:MULE_LIB,点击“OK”

(6)点击“Add
JARs”,选择Mule主目录下的
/lib/mule,选择所有的jar包,点击“Open”

(7)点击“OK”,点击“Finish”

(8)点击“Add
Variable”,点击“Configure
Variables”,点击“New”

(9)输入:M2_REPO,选择一个目录。默认地,Maven会在刚才使用mvn命令编译时,

把下载的jar包放到:C:/Documents
and Settings/Administrator/.m2/repository 目录下,Administrator是我的用户名,这里我设置的目录是C:/.m2/repository,你可以把那个目录下的所有copy到这个目录下。 点击“Folder”,选择C:/.m2/repository。

(10)点击“OK”,点击“yes”,点击“OK”。设置完成。

7.1.6 运行这个工程

(1)右击工程,选择“Run->Run
Configurations”

(2)双击”JavaApplication”,把名字改为:Hello,选择main
class为:org.mule.MuleServer。

(3)在Arguments选项表中,在Program
Arguments框中输入-config
conf/hello-config.xml

(4)点击“Apply”,“Run”。运行例子,如图:

备注:

运行“Run
as Mule Server”报错“A
Mule runtime distribution must be configured”的解决办法:在eclipse的classpath界面里面添加libray ;右键工程---->properties----->Java Build
Path----->Libraries------>Add Library------>Mule Classpath

7.2 ECHO例子分析

7.2.1 概述

这个演示了“如何通过使用一个简单的web
service flow,让我们了解Mule
ESB 组件;在这个例子中,这个组件被了解是通过使用CXF
的JAX-WS
web Service.

1、  通过两种方式接入(红色线):一个是System.in,
一个是Soap(http)方式

2、  每种接入都可以通过接入器(Connector)经过NMR(Normalized
Message Router)规范化的消息路由转发后,进行输出,目前通过两种方式:System.out
和Soap(http)方式。

7.2.2 运行应用

简单的拷贝预构建的档案资料(mule-example-echo.zip)到应用文件夹($MULE_HOME/apps),并启动mule。去通过浏览器访问web
service 调用:

http://localhost:65082/services/EchoUMO/echo/text/hello

通过在控制台按“CTR-C”停止mule;

7.2.3 编译例子

依靠你正在使用的编译工具(Ant or
Maven) ,你能够通过简单的运行”ant”
或”mvn”去编译样例,这将编译样例的类文件,产生一个应用的zip文件,拷贝这个zip文件到
 $MULE_HOME/apps.

7.2.4 The
Echo 组件

Echo
Service 是基于一个POJO
组件,该组件是使用JAX-WS
注解方式被注解,并作为一个web
service 被暴露出来,该Web
Service 是在MULE
使用基于流的配置之上的。组建的初始化如下:

public class Echo

{

    public String
echo(String string)

    {

        return
string;

    }

}

通过增加JAX-WS注解的方法,我们能把该类方法发布成一个web service, 并具体说明输入参数和响应是怎样被映射的。

@WebService

public class Echo

{

    @WebResult(name="text")

    public String
echo(@WebParam(name="text") String string)

    {

        return
string;

    }

}

7.2.5 配置流程(Configuring the Flow)

配置Service
,先添加<flow>元素到Mule
XML 配置文件,并提供name
属性,用<component>元素具体指定服务组件的类;对于从spring注册库引用的对象,应当使用内嵌的<spring-object>来代替;

    <flow
name="EchoFlow">

        <component>

            <singleton-object
class="org.mule.example.echo.Echo" />

        </component>

</flow>

类的属性必须是完全的合格的类路径名,名称属性对服务来说必须是唯一的名称。

7.2.6 调用流程(Invoking the Flow)

         当前流程是有效的,但是没有入口点,通过URL执行它,并不能得到反馈信息。首先必须为service配置一个端点endpoint,
以使它能被调用。当运行实例时,通过http发送一个request去调用EchoFlow,一个相应通过Http
channel被返回。Mule
Http Transport管理这个交互。

去配置这个流程,我们已经创建一个接入点<inbound-endpoint>,来实现请求相应request-response.下面是包括endpoint的EchoFlow配置:

    <flow
name="EchoFlow">

        <inbound-endpoint
address="http://localhost:65082/services/EchoUMO"
exchange-pattern="request-response"/>

        <component>

            <singleton-object
class="org.mule.example.echo.Echo" />

        </component>

</flow>

         如果现在去触发流程,将会得到一个响应,它将返回一个URL
PATH 给你。然而EchoFlow仍不知道怎么去处理Web
Service的调用,首先你需要去采用一个使用CXF的过滤器。

7.2.7 暴露CXF类型Web Service
(Exposing as a Web Service with CXF)

         CXF已经内置了对GET
requests 约定的支持,使用的是下面的句法:

http://host/service/OPERATION/PARAM_NAME/PARAM_VALUE

所以,可以通过输入下面的URL来运行该样例:

http://localhost:65082/services/EchoUMO/echo/text/hello

为了使CXF能够去处理消息, 像下面去更新EchoFlow

<flow name="EchoFlow">

<inbound-endpoint
address="http://localhost:65082/services/EchoUMO"
exchange-pattern="request-response"/>

     <cxf:jaxws-service
serviceClass="org.mule.example.echo.Echo"/>

<component>

            <singleton-object
class="org.mule.example.echo.Echo" />

</component>

</flow>

现在如果去触发URL,将会得到适当的响应消息。也可以去请求WSDL,它是组件运行需要的全部的配置信息。去看整体内的配置文件,打开目录” examples/echo/src/main/app/ directory under your Mule
installation directory”下的mule-config.xml文件.

7.2.8 添加Stdio端点(Adding a
STDIO endpoint)

在MULE 之前的版本中,echo实例支持在命令行输入信息时的提示,当你输入一些信息时,你正通过System.in
提供输入数据调用服务,在那时你输入的数据经由Systme.out被不停的回应回来。MULE
STDIO Transport管理这个交互。

去配置STDIO 支持该流程,你需要在你的配置文件中增加STDIO的命名空间,然后使用”one-way”交换模式详细制定inbound

outbound端点。

    <flow
name="EchoFlow">

        <stdio:inbound-endpoint
system="IN" exchange-pattern="one-way"/>

        <component>

            <singleton-object
class="org.mule.example.echo.Echo" />

        </component>

        <stdio:outbound-endpoint
system="OUT" exchange-pattern="one-way"/>

</flow>

<outbound>元素允许一个或多个outbound路由元素被配置去控制如何、哪里的消息被发送,一旦他们被组件处理时。在这个例子中,所有的消息被经由System.out发送。

最后,如果你想去覆默认被传输具体指定在inbound
endpoint上配置,你可以去配置一个连接器。在这个例子中,让我们覆盖stdio
传输器默认的链接器,于是提醒用户输入下面的内容:

<stdio:connector
name="SystemStreamConnector"

                 promptMessage="Please
enter something: "

                 messageDelayTime="1000"/>

7.2.9 增加多个接入点

有几个方法对流程增加多个接入点,如果他们是跟随统一个处理器链,你可以使用<composite-source>,像下面所示:

    <flow name="EchoFlow">

        <composite-source>

            <stdio:inbound-endpoint
system="IN" exchange-pattern="one-way"/>

            <vm:inbound-endpoint
path="echo" exchange-pattern="request-response"/>

        </composite-source>                          

        <component>

            <singleton-object
class="org.mule.example.echo.Echo" />

        </component>

</flow>

然而,如果你有明确的端点消息处理器去应用,像CXF,一个选择是去使用复合流程,在复合流程中,你引用的一个流程是来自另外的一个流程中。这个方法,你可以中止流程通过端点。看下面的例子:

    <flow
name="EchoStdio">

        <stdio:inbound-endpoint
system="IN" exchange-pattern="one-way"/>

     <flow-ref
name="EchoComponent"/>

        <stdio:outbound-endpoint
system="OUT" exchange-pattern="one-way"/>

    </flow>

 

    <flow
name="EchoComponent">

        <component>

            <singleton-object
class="org.mule.example.echo.Echo" />

        </component>

</flow>

    <flow
name="EchoWebService">

        <inbound-endpoint
address="http://localhost:65082/services/EchoUMO"
exchange-pattern="request-response"/>

        <cxf:jaxws-service
serviceClass="org.mule.example.echo.Echo"/>

     <flow-ref
name="EchoComponent"/>

</flow>

EchoStdio
和EchoWebService
提供两种不同的接入点。他们都死通应用执行的EchoComponent。

7.2.10 使用一个服务来配置(Configuring using a Service)

作为选择,流程能够使用一个服务来配置,去配置这个服务,你增加一个<service>元素到你的MULE
XML 配置文件中,并提供名字属性。通过使用<component>元素具*定服务组件的类。然后和之前的一样增加http
端点和消息处理器;

    <model
name="echoSample">

        <service
name="EchoUMO">

            <inbound>

                <inbound-endpoint
address="http://localhost:65082/services/EchoUMO"

                                  exchange-pattern="request-response">

                    <cxf:jaxws-service
/>                 

                </inbound-endpoint>

            </inbound>

            <component>

                <singleton-object
class="org.mule.example.echo.Echo"/>

            </component>

        </service>

</model>

在服务配置中,对CXF来说ServiceClass是不需要的。这是因为在这个配置中,CXF是能够自动发现组件的。基于流的配置目前还不支持,所以serviceClass必须被明确的制定。

7.3 HelloWorld 例子分析

7.3.1 概述

该部分主要描述Hello
World例子的配置和行为。

当我们在浏览器中输入预定义的地址
(http://localhost:8888/?name=Ross) 时 Mule解释输入值中“?name=”后的部分,进行验证,使greeeter.java
类去添加一些文本到字符串中,然后传递到另外一个flow,第二个flow继续添加文本,然后通过浏览器传回来;以上做的这些知识简单的方式,但是这个例子使用了flows和http传输去说明消息路由和转换器在Mule中的引用。

7.3.2 运行应用

如果正在使用MULE
IDE 环境,可以创建一个基于Hello
World例子的工程,然后通过Eclipse来运行;

如果没有使用MULE
IDE环境,简单的拷贝预构建的档案资料(mule-example-hello-3.0.0.zip)到应用文件夹($MULE_HOME/apps),并启动mule。去通过浏览器访问web
service 调用: http://localhost:8888/?name=Ross

通过在控制台按“CTR-C”停止mule;

7.3.3 编译例子

同上

7.3.4 如何工作的

Hello
World应用被配置在mule-config.xml中,这个文件存在于mule 根目录下的examples/hello/src/main/app.
该部分主要是通过配置和JAVA资源文件的调用实现的。

7.3.5 资源文件

消息的文本是从hello-example-messages.properties文件中获得的,该文件存在于Message
text is taken from the hello-example-messages.properties file in the
examples/hello/src/main/resources/messages文件夹, 以下是资源文件的内容:

1=, how are you?  2=Hello 3=Please enter your name:

4=Please provide a valid name
with at least one character!

在同一个文件夹中,该属性文件有翻译的版本可以替换,例如你可以德语的字符串替换英语的字符串。

7.3.6 类

Mule 配置文件唤起两个JAVA类去处理消息,首先是Greeter类,这个类用一个方法从LocalMessage类去推送字符串“hello”,字符串内容来自属性文件。Greeter类的方法
greet() 然后去追加人名。

public class Greeter

{

private
String greeting = "";

public
Greeter()

{

greeting = LocaleMessage.getGreetingPart1();

}

public
Object greet(NameString person)

{

Object payload = person;

if (person.isValid())

{

person.setGreeting(greeting);

}

else

{

payload = new Exception(LocaleMessage.getInvalidUserNameError());

}

return payload;

}

}

第二个类是ChitChatter
, 这个类实现附加字符串”,how
are you?”.

public class ChitChatter

{

private
String chitchat = "";

public
ChitChatter()

{

chitchat = LocaleMessage.getGreetingPart2();

}

public void
chat(ChatString string)

{

string.append(chitchat);

}}

7.3.7 把类连接起来

在Hello
World中,调用这些类的配置文件,在composite元素中组成了3个接入点,去接受HTTP、Servlet、和VM请求;

Hollo
World flow 使用这个composite元素去获得在浏览器中输入的名字,调用greeter类,路由意外的错误去分离被调用的systemErorHandler中的handler.

<flow name="Hello
World">

<composite-source>

<!-- Incoming HTTP requests -->

<inbound-endpoint address="http://localhost:8888"
transformer-refs="HttpRequestToNameString"
exchange-pattern="request-response">

<not-filter>

<wildcard-filter
pattern="/favicon.ico"/>

</not-filter>

</inbound-endpoint>

<!-- Incoming Servlet requests -->

<inbound-endpoint address="servlet://name"
transformer-refs="HttpRequestToNameString" exchange-pattern="request-response">

<not-filter>

<wildcard-filter pattern="/favicon.ico"/>

</not-filter>

</inbound-endpoint>

<!-- Incoming VM requests -->

<vm:inbound-endpoint path="greeter"
transformer-refs="StringToNameString"
exchange-pattern="request-response"/>

</composite-source>

...

</flow>

接入点元素使用http传输 接受接入的消息。在问候者受到消息之前,Transformer-refs属性指定接入转换器被调用。该转换器早先在配置文件被定义:

<custom-transformer
name="HttpRequestToNameString"
class="org.mule.example.hello.HttpRequestToNameString"/>

该转换器转换从Http
Connector 收到的Http
Request 为NameString
对象,该对象是类Greeter的Greet()方法预期的数据类型。当你指定逐渐的时候,可以不必指出一个具体类中的一个具体的方法—Mule
能决定基于消息数据类型的适合的方法。

Greeter类预决定对输入的用户“问候”后,消息被匹配在端点vm://chitchatter.
这是调ChitChat
flow, 同时调用定制的转换器NameStringToChatString,转换NameString
object为ChatString
object.

<flow
name="ChitChat">

<vm:inbound-endpoint path="chitchatter"
transformer-refs="NameStringToChatString"

responseTransformer-refs="ChatStringToString"
exchange-pattern="request-response"/>

<component class="org.mule.example.hello.ChitChatter"/>

</flow>

去更多的演示转换器,ChitChatter类期待一个ChatString对象,于是我们有了一个NameStringToChatString转换器,在组件接收到消息之前,去转换消息有效部分从NameString 为ChatString。消息被接收在vm://chitchatter上,这个端点是关于Greeter类分发它的消息。

消息被处理后,该消息被作为Http响应信息被发送,这个组件上的响应转换会记录。甚至认为没有接出提供者,再有一个转换器ChatStringToString,设置 Even
though there is no outbound provider, there is one more transformer,
ChatStringToString,它转换有效负载从ChatString
为 平常的string ,所以他能被Http 传输处理,在http响应中被显示。

注释:JAVA类没有任何的路由逻辑,他们是通过Mule的配置文件连接在一起的,能实现在JAVA类,WebService等之间传递消息

7.3.8 配置Servlet 传输

在Web容器中部署MULE是很常见的,使用Web
Server 代替Mule
Http Transport去管理Http
connection。通过Servlet的方式调用Hello 服务,提交一个Web 表单,如下:

<form
method="POST" action="/mule-examples/rest?endpoint=greeter">

<input
type="text" name="payload"/>

<input
type="submit" value="POST" />

</form>

使用Servlet传输,下面的端点能被添加到配置文件中 (注释: the
REST endpoint currently only works on the servlet transport only since the
HttpRequestToParameter transformer only supports servlets.):

<inbound-endpoint
address="servlet://name" transformer-refs="HttpRequestToNameString" exchange-pattern="request-response">

<not-filter>

<wildcard-filter
pattern="/favicon.ico"/>

</not-filter>

</inbound-endpoint>

<inbound-endpoint
address="servlet://rest" transformer-refs="HttpRequestToParameter
StringToNameString"responseTransformer-refs="PlainTextResponseTransformer" exchange-pattern="request

7.3.9 配置VM 传输

这个实例也演示在Mule
程序中如何连接服务、如何使用mule
Client

<vm:inbound-endpoint
path="greeter" transformer-refs="StringToNameString" exchange-pattern="request-response"/>

然后用Mule
Client API 去调用服务:

MuleClient client = new MuleClient(muleContext);

client.send("vm://greeter",
"Ross", null);

MuleMessage
response = client.send("vm://greeter",
"Ross", null);

System.out.println("response
= " + response.getPayload());

Mule ESB学习【转-结合了网络上的一些资源】的更多相关文章

  1. &lbrack;原创&rsqb;java WEB学习笔记50:文件上传案例

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  2. &lbrack;原创&rsqb;java WEB学习笔记49:文件上传基础,基于表单的文件上传&comma;使用fileuoload 组件

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  3. EnjoyingSoft之Mule ESB开发教程系列第五篇:控制消息的流向-数据路由

    目录 1. 使用场景 2. 基于消息头的路由 2.1 使用JSON提交订单的消息 2.2 使用XML提交订单的消息 2.3 使用Choice组件判断订单格式 3. 基于消息内容的路由 4. 其他控制流 ...

  4. EnjoyingSoft之Mule ESB开发教程第二篇:Mule ESB基本概念

    目录 1. 使用Anypoint Studio开发 2. Mule ESB Application Structure - Mule ESB应用程序结构 3. Mule ESB Application ...

  5. EnjoyingSoft之Mule ESB开发教程第六篇:Data Transform - 数据转换

    目录 1. 数据转换概念 2. 数据智能感知 - DataSense 3. 简单数据转换组件 3.1 Object to JSON 3.2 JSON to XML 3.3 JSON to Object ...

  6. NASNet学习笔记——   核心一:延续NAS论文的核心机制使得能够自动产生网络结构;    核心二:采用resnet和Inception重复使用block结构思想;    核心三:利用迁移学习将生成的网络迁移到大数据集上提出一个new search space。

    from:https://blog.csdn.net/xjz18298268521/article/details/79079008 NASNet总结 论文:<Learning Transfer ...

  7. Snail—iOS网络学习之得到网络上的数据

    在开发项目project中,尤其是手机APP,一般都是先把界面给搭建出来.然后再从网上down数据 来填充 那么网上的数据是怎么得来的呢,网络上的数据无非就经常使用的两种JSON和XML 如今 大部分 ...

  8. EnjoyingSoft之Mule ESB开发教程第一篇:初识Mule ESB

    目录 1. Mule ESB基本介绍 2. Mule ESB社区版和企业版 3. Mule ESB常用场景 4. Mule ESB软件安装 客户端安装 服务端安装 5. 第一个Mule ESB应用- ...

  9. JWebFileTrans&lpar;JDownload&rpar;&colon; 一款可以从网络上下载文件的小程序&lpar;三&rpar;,多线程断点下载

    一 前言 本篇博客是<JWebFileTrans(JDownload):一款可以从网络上下载文件的小程序>系列博客的第三篇,本篇博客的内容主要是在前两篇的基础上增加多线程的功能.简言之,本 ...

随机推荐

  1. SQL注入处理

    /// <summary> /// SQL注入处理 /// </summary> /// <param name="rowFilter">&lt ...

  2. Docker的容器创建以及基本命令

    1. 使用docker run创建docker容器,(docker命令都是以docker开头的)安装完docker后,大多数情况下,本机上面一般没有docker镜像的,执行docker run的时候一 ...

  3. linux中ll和du的区别

    首先,明确一个概念,linux中目录其实也是一个文件,它存储了一张表,该表就是该目录文件下,所有文件名和inode的映射关系. 其中inode和数据块block的关系http://c.bianchen ...

  4. POJ 2021

    #include <iostream> #include <string> #include <algorithm> #define MAXN 105 using ...

  5. S2SH商用后台权限系统第二讲

    各位博友: 您好!我们今天接着上一讲继续学习S2SH商用后台权限系统,本讲我们重点设计下整个系统所需要的表结构. 用户表:记录用户信息. 角色表:标记用户归属的角色信息. 菜单表:或者叫模块表,记录整 ...

  6. UI自动化测试表单重要代码

    public class frame { public static void main(String[] args) { // TODO Auto-generated method stub Sys ...

  7. &lbrack;译&rsqb;ASP&period;NET Core 2&period;0 视图组件

    问题 如何在ASP.NET Core 2.0中使用视图组件? 答案 新建一个空项目,修改Startup类并添加MVC服务和中间件: public void ConfigureServices(ISer ...

  8. 1&period;C和C&plus;&plus;区别&comma;以及const分析

    从本章起开始从0学习C++,本章主要内容: 1)C和C++的基本区别 2)C和C++的const区别 1.C++和C区别 1.1 C++更强调语言的实用性,所有变量都可以在需要时再定义 比如: ;i& ...

  9. scrapy爬取全部知乎用户信息

    # -*- coding: utf-8 -*- # scrapy爬取全部知乎用户信息 # 1:是否遵守robbots_txt协议改为False # 2: 加入爬取所需的headers: user-ag ...

  10. webpack中 resolve&period;alias 配置,&commat;import相关踩坑

    1.在使用webpack打包项目时,可以在配置文件中配置resolve.alias来定义一些绝对路径,方便在项目中灵活使用路径,举例如下: resolve: { extensions: [‘.js‘, ...