1. 程式人生 > >MULE ESB 配置(三)

MULE ESB 配置(三)

目錄[-]


mule號稱開源ESB的最好實現,研究mule也有段時間了, 在“浩如煙海”的xml配置中,保持清醒的頭腦確實不容易。
 作為學習筆記之一,記錄一下一個mule簡單應用的實現。

需求:給指定的email地址傳送郵件.

 一:基本實現:

 1: 實現命令列輸入傳送email:
 為了能在命令列接受輸入, 需要配置一個輸入輸出聯結器:

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

 2:配置一個UMO,把輸入的內容放入一個佇列:

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>

outbound節點的配置, 把輸入的內容(String) 路由到一個叫“content”的queue中, 此queue為jvm中的記憶體佇列。

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="[email protected]"    
                        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="[email protected]"    
                    subject
="test for mule email bridge!"/></pass-through-router></outbound></service></model></mule>

相關class如下:
自定義訊息轉換器:

publicclass 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):

publicclass SmtpConnector extends org.mule.transport.email.SmtpsConnector     
{     
    
    @Override    
    
protectedvoid 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):     
[email protected], I come from Wuhan city
!

二: 升級:增加一個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="[email protected]"    
                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;     
    
publicclass EmailModel     
{     
    
private String address;     
    
private String content;     
        
    
public EmailModel(String address, String content)     
    {     
        
this.address = address;     
        
this.content = content;     
    }     
    
public String getAddress()     
    {     
        
return address;     
    }     
    
publicvoid setAddress(String address)     
    {     
        
this.address = address;     
    }     
    
public String getContent()     
    {     
        
return content;     
    }     
    
publicvoid setContent(String content)     
    {     
        
this.content = content;     
    }     
    @Override    
    
public String toString()     
    {     
        
// TODO Auto-generated method stub     return"address="+ address +", content="+ content;     
    }     
}    

EmailComponent.java
需要說明的是:
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> package co.mule.mail;     
    
import org.mule.RequestContext;     
import org.mule.transport.email.MailProperties;     
    
publicclass 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());     
        
returnnew EmailModel(address, content);     
    }     
}    
package co.mule.mail;     
    
import org.mule.api.transformer.TransformerException;     
import org.mule.transformer.AbstractTransformer;     
    
publicclass 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();     
    }     
    
}    

三:繼續升級:不滿足於在命令列輸入, 需要在瀏覽器輸入, 也就是釋出一個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:[email protected],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();  
        // creat 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"