读书人

ESB之旅(示范echo、hello、StockQuot

发布时间: 2012-10-10 13:58:11 作者: rapoo

ESB之旅(示例echo、hello、StockQuote分析)

???? 一、Echo?源码分析

? 看mule的配置加载部分,配置构造器接口org.mule.api.config.ConfigurationBuilder CB:

? 一系列的CB专用来处理MuleContext,怎么处理?配置它、配置什么呢?:endpoint、connector、 service、transformer..这些都是什么?mule的运行时组件artifact,所以说CB就是根据配置来向MuleContext注入各种组件的,注入完成CB的任务也就完了。从CB这个接口看出mule的配置构造和运行时环境分离的很清楚,其主方法:void configure(MuleContext muleContext)方法,MuleContext是外部传递进来的,方法只需要对其进行配置处理即可,方法也没有返回。CB本身则持有配置文件信息,在构造它的时候就传入了。

? 往下的子类AbstractConfigurationBuilder运用典型的模板方法,一层层有选择的做出实现。AbstractConfigurationBuilder这一层可以说仅仅是实现了isConfigured()方法,同时把配置处理的具体实现延迟到子类的doConfigure(muleContext)和applyLifecycle两个方法去实现。

? 再往下的子类AbstractResourceConfigurationBuilder引入了ConfigResource[],再为父类的configure方法添油加醋,ConfigResource是把配置作为资源的包装类,在这里允许输入多个配置文件。如果你要写自定义的配置构造器那么就应该从AbstractResourceConfigurationBuilder继承,它主要是把一系列配置封装为configResources.

? 继续往下就到了3个配置构造器的具体实现类了,AutoConfigurationBuilder子类上面也提了,主要是为了实现多个配置文件和多种配置生效的,可以轮番处理muleContext不多说了。

? 最主要的还是位于spring模块mule-module-spring-config.jar下的SpringXmlConfigurationBuilder,支持以spring + mule namespace方式进行配置,同时也支持逗号分隔的多个配置文件。到了这里就开始依赖spring了,而且直接用的是ApplicationContext(这也是Rod大力推崇的方式。实际上spring的核心ioc功能和现在广大Mis编程人员根本没tmd一点狗屁关系、像mule这样的第三方框架才算是重度依赖spring ioc的。所以如果你是广大Mis编程人员的一员而且对spring越来越感到鸡肋就不用奇怪了)。它默认直接采用了一个自带配置文件default-mule-config.xml,这个文件在mule-module-spring-config-2.2.1.jar包中。SpringXmlConfigurationBuilder有两个重要成员:

??? private Registry registry;——两个对象容器?还有一个是MuleContext。
??? private ApplicationContext parentContext;

? Registry这个东西是个注册库,很多对象都放在其中包括inbound、outbound、transformer、expressionEvaluator...你也可以在其中存放你想在component之间共享的对象,像这样使用它:

? muleContext.getRegistry().registerObject("foo", new Foo());

? 甚至于连spring的ApplicationContext也放在里面,你也可以拿出来用:

? ApplicationContext ac = (ApplicationContext)muleContext.getRegistry()

??? .lookupObject(SpringRegistry.SPRING_APPLICATION_CONTEXT);
? FunctionalTestComponent testComponent = (FunctionalTestComponent) ac.getBean("FTC");

? 如果是springRegistry,它直接持有了ApplicationContext,那么通过它去找ApplicationContext的话就不必再搜索了直接返回它持有的ApplicationContext。

?

? 由于对cxf的入站端点有点感兴趣,所以就以Echo的cxf版本配置为例来对Mule进行运行时分析。前面的ESB选型提过如何构建mule项目,结果纠缠到spring上去了,这里再提一下:

????? 1、在eclipse构建一个普通的Mule221项目(不是mule ide创建的mule项目,因为这么用的话链接不到mule源码上去)、将mule整个源码目录mule-2.2.1-src作为源码目录之一。

????? 2、导入jar前面提过(除了mule目录下的mule源码jar包,其他的基本都导入),还是有编译错误不过不影响主体。再新建自己的源码目录src和配置目录conf,都作为源码路径。

????? 3、把echo-cxf-config.xml扔到conf下、在src新建类:com.test.MainMule:

public static void main(String[] args) {try {ConfigurationBuilder configBuilder = new SpringXmlConfigurationBuilder("echo-cxf-config.xml");MuleContext muleContext = new DefaultMuleContextFactory().createMuleContext(configBuilder);                                muleContext.start();} catch (ConfigurationException e) {e.printStackTrace();} catch (InitialisationException e) {e.printStackTrace();} catch (MuleException e) {e.printStackTrace();}}

?

? 以纯代码方式启动mule(要打包为可执行jar包也可以写这么一个类并作为jar包运行类),这些启动代码也基本上就是org.mule.MuleServer的做法(在eclipse中右击xml文件点选以Mule Server运行也是运行的MuleServer)。射了些断点,开练:

? 1、SpringXmlConfigurationBuilder构造函数断点:直接调用父类AbstractResourceConfigurationBuilder的构造子、以“,; ”逗号分号或空格作为分隔符来分隔出可能的多个配置文件、我们知道AbstractResourceConfigurationBuilder的唯一任务是把配置资源封装为ConfigResource、它首先去文件系统搜索配置文件,调试看到它使用FileUtils定位到路径D:\EclipseWorkSpace\Mule221\echo-cxf-config.xml(可以看出它找的是user.dir系统属性,也就是用户当前工作目录,说白了代码就是:System.getProperty("user.dir"),用户当前工作目录系统属性就是跑你程序的项目根目录)、找不到、然后用ClassUtils去classpath下搜索配置文件(还是老套的spring做法:Thread.currentThread().getContextClassLoader().getResource("..."))、定位到路径file:/D:/EclipseWorkSpace/Mule221/classes/echo-cxf-config.xml、找到了、构建出的ConfigResource对象:resourceName=echo-cxf-config.xml;url=file:/D:/.../echo-cxf-config.xml。从这个流程我们看出,配置文件可以扔在classpath下、或者项目根目录下、只要传入配置文件的文件名即可定位到。

? o了,ConfigurationBuilder configBuilder = new SpringXmlConfigurationBuilder("echo-cxf-config.xml");执行完毕,仅仅是定位到了配置文件,还没解析。

?

? 2、MuleContext muleContext = new DefaultMuleContextFactory().createMuleContext(configBuilder);构建mule上下文断点:先创—efaultMuleContextFactory、接着用DefaultMuleContextFactory开始创建MuleContext、看得出来DefaultMuleContextFactory需要俩东东来创建MuleContext:一个是ConfigurationBuilder配置构造器、一个是MuleContextBuilder上下文构造器,实际类型分别是SpringXmlConfigurationBuilder 和 DefaultMuleContextBuilder(创建出来的“空白”MuleContex为DefaultMuleContext、内部成员: MuleConfiguration、 LifecycleManager、 WorkManager、 WorkListener、 ServerNotificationManager。对于“空白”MuleContex来说全是Default默认的)、随后将MuleContext射入MuleServer(这才发现MuleServer内的属性和方法成员都是static的!这么看MuleServer可以作为在程序中的任意位置定位mule上下文的工具,仔细一看其实人家早在MuleServer的muleContext成员上写注释了:a?handle to the local MuleContext can be obtained from anywhere by calling?MuleServer.getMuleContext(),如果这段注释是中文的我应该立马能理解,英文的就隔一层了)

? “空白”DefaultMuleContext的成员概览:

? DefaultMuleConfiguration 配置信息载体、MuleContext启动以后变为immutable。似乎还包括了一些涉及消息样式的变量:synchronous、systemModelType = "seda"、responseTimeout,并且在applySystemProperties()方法中读取了一些mule特定系统属性,从中还能看出mule不支持crimson的xml解析器。另外里面的getMuleHomeDirectory()方法是取得名为"mule.home"
的系统属性而不是环境变量,这样应该是什么也取不到的。还有这个类要取得上下文也是靠:MuleServer.getMuleContext()

? WorkManager 作业管理器、JCA作业管理器实现、用于管理mule的components组件和connectors连接器的线程分派。涉及一系列线程、并发、osgi的东西

JCA,英文全称为Java Connector Architecture,中文全称为Java连接器体系。JCA提供以下功能:

▲ 连接缓冲池:EIS连接通常属于昂贵的资源,创建EIS连接需要大量的时间开销。连接池使得应用服务器能够创建和共享EIS应用的连接,使得应用能够更高效地使用昂贵的连接资源。
▲ 事务管理:事务管理能力使得EIS应用能够获取应用服务器提供的事务环境的支持,使得服务器能够把EIS系统的事务作为一个单元管理。
▲ 安全:安全接口的实现允许应用服务器在不影响EIS特有安全机制的情况下,对整体安全性进行有效的管理。验证、授权和安全关联都属于该接口包含的范围,它们都属于为JCA适配器和J2EE应用服务器内建的服务。
▲ 公共的客户端接口:JCA还定义了用户级的编程接口,称为公共客户端接口(CCI,Common Client Interface)。这个接口集在JCA 1.0中是可选的,允许EIS客户程序的开发者按照一种标准的方式,连接目标EIS系统,或与目标EIS交互(执行命令并获取结果)

还可以参考:JCA: Java步入应用集成时代

? WorkListener 作业监听器、默认用于处理作业管理器中的作业运行异常。

? LifecycleManager 生命周期管理器、负责管理服务器不同的生命周期阶段并管理阶段间的过度。

? NotificationManager 服务端通知管理器

? DefaultExpressionManager 表达式管理器、提供对mule配置中的表达式的全局访问点,表达式包括Xml、Java、scripting?、 annotations。在这里用户可以注册或解除注册ExpressionEvaluator表达式计算器。

?

? 接着调用:DefaultMuleContext.initialise()方法、方法先进行生命周期校验:GenericLifecycleManager.checkPhase(Initialisable.PHASE_NAME);如果启动周期已执行过了或者正在执行、当前正在执行某周期...则抛出InitialisationException异常。

? 接着创—efaultRegistryBroker注册库代理、往里面注入了一个TransientRegistry。再把这个注册库代理注入MuleRegistryHelper注册库帮助类,总之目的就是代理了多个注册库的操作,比如MuleRegistryHelper里面就能看到lookupConnector、lookupEndpoint、lookupTransformer、lookupService...

? 接着调用workManager.start();实际上是启动了:MuleWorkManager(ThreadingProfile.DEFAULT_THREADING_PROFILE, "MuleServer"),调用了:edu.emory.mathcs.backport.java.util.concurrent.ExecutorService
?workExecutorService = threadingProfile.createPool(name);? 这里涉及到org.mule.api.config.ThreadingProfile线程描述?接口,这个接口注释:mule使用了一些不同的池、比如service线程和消息分发器,使用这个接口便于配置这些池、池是通过ImmutableThreadingProfile.createPool(String, ThreadingProfile)创建的、ImmutableThreadingProfile是本地ThreadingProfile拷贝的只读实现?

?

? 接着是一系列NotificationManager操作、lifecycleManager.firePhase(this, Initialisable.PHASE_NAME);

?

?

? 二、Hello World (主要演示了两个service component链式合作处理一条消息和消息格式转换)

??? 1、示例翻译:

? 展示了如何配置多个service components——它们与一个请求交互(就是说二者合作以链式方式先后处理一个请求消息,处理的方式是添加消息的内容),以及如何管理事件转换(所谓事件就是消息,这里的事件转换是指消息格式的转换,比如从stdio标准输入中输入的字符串转换为一个java bean对象以及不同java bean之间的转换)。这个例子还使用了属性文件来配置i18n国际化的消息文字。还有演示了出站过滤路由。

? 使用了两个类也就是两个service component组件来先后处理消息,第一个是Greeter 类,它的greet()方法 使用LocalMessage 来从上面提到的属性文件获取greeting问候语,然后把问候语"Hello"加到你在控制台输入的名字之前(这样它就第一次修改了消息的内容)。第二个是ChitChatter类,它的chat方法 把", how are you?"加到消息内容之后(这样它又一次修改了消息内容):

<service name="GreeterUMO">  <inbound>    <stdio:inbound-endpoint system="IN" transformer-refs="StdinToNameString"/>  </inbound>  <component name="code"><custom-transformer name="StdinToNameString" name="code"><service name="ChitChatUMO">  <inbound>    <vm:inbound-endpoint path="chitchatter" transformer-refs="NameStringToChatString"/>  </inbound>  <component transformer-refs="ChatStringToString" />    </pass-through-router>  </outbound></service>

?

? ChitChatUMO服务组件又配置了两个转换器:NameStringToChatString、ChatStringToString。ChitChatter组件类的输入参数为 ChatString 类型,所以NameStringToChatString转换器将消息格式从NameString转为 ChatString、然后再调用 ChitChatter组件(流程:GreeterUMO ->?vm://chitchatter -> NameStringToChatString -> ChitChatter -> ChatStringToString -> System.out)

? 注意Java bean是不含有任何路由逻辑的, Mule配置文件将它们组织到一起,任何已有的pojo、web services都可以照此办理并在它们之间传输消息。

?

?

? 三、Stock Quote (演示了如何调用ASPX web service、使用XSLT转换、反序列化StockQuote Java bean以及使用REST和SOAP调用服务。例子需要访问互联网上的公共.NET服务、主要是咱也不知道人家都有哪些股票代码有数据。源码就不看了)

??? 1、示例翻译:通过System.in接收股票代码、调用StockQuote服务、通过XSLT转换器将返回结果转换格式、通过XmlToObject转换器再将结果转换为StockQuote类型、随后将股票报价打印到System.out

? (例子用到了类似spring的属性占位符特性来从配置文件取得一些信息、配置多个转换器并“串联”起来、其中还用到xslt转换器)

<model name="Sample-Rest">    <service name="HTTPPostSample">      <inbound>        <vm:inbound-endpoint path="stockquote"               responseTransformer-refs="ToString XmlDecoder Xslt XmlToObject"/>      </inbound>      <http:rest-service-component               serviceUrl="http://www.webservicex.net/stockquote.asmx/GetQuote"               httpMethod="POST">        <http:payloadParameterName value="symbol"/>      </http:rest-service-component>    </service>  </model>

? 配置当中还用到所谓的REST service component , 它使用了REST服务包装器代理了一个REST服务、这样使得该service服务看上去似乎是本地的component组件一般(和CXF的web服务包装器差不多),REST服务包装器有一些配置属性:serviceUrl就是 访问REST服务的url、payloadParameterName是传参名 ,本例中只有一个参数"symbol"——股票代码、httpMethod是方法名 ——GET或POST。

? 我随便传了个代码过去返回了无数据的xml:

<string><StockQuotes><Stock><Symbol>002339</Symbol><Last>0.00</Last><Date>N/A</Date><Time>N/A</Time><Change>N/A</Change><Open>N/A</Open><High>N/A</High><Low>N/A</Low><Volume>N/A</Volume><MktCap>N/A</MktCap><PreviousClose>N/A</PreviousClose><PercentageChange>N/A</PercentageChange><AnnRange>N/A - N/A</AnnRange><Earns>N/A</Earns><P-E>N/A</P-E><Name>002339</Name></Stock></StockQuotes></string>
Web Service版本:

? Web Service版和REST版原理类似,只是服务配置是不同的。Web Service版显式配置了outbound pass-through路由,它将输入从一个endpoint直接传输到outbound Axis endpoint,不作任何改变或处理。另外outbound endpoint向Stock Quote service股票报价服务请求时是带参数的。

<model name="Sample-SOAP">  <service name="serviceProxy">    <inbound>      <vm:inbound-endpoint path="stockquote"                  responseTransformer-refs="ToString XmlDecoder Xslt XmlToObject"/>    </inbound>    <outbound>      <pass-through-router>        <axis:outbound-endpoint address="http://www.webservicex.net/stockquote.asmx?method=GetQuote"                  responseTransformer-refs="XmlDecoder Xslt XmlToObject"                  soapAction="[methodNamespace][method]">          <axis:soap-method method="qname{GetQuote:http://www.webserviceX.NET/}">            <axis:soap-parameter parameter="symbol" type="string" mode="IN"/>            <axis:soap-parameter parameter="GetQuoteResult" type="string" mode="OUT"/>          </axis:soap-method>        </axis:outbound-endpoint>      </pass-through-router>    </outbound>  </service></model>

读书人网 >软件架构设计

热点推荐