- 1. 总述
- 2. JBossESB的概念
-
- 2.1.
Message (消息)
- 2.2.
Service (服务)
- 2.3.
Listeners, Gateway
- 2.4.
Couriers
- 2.5.
Actions
- 2.6.
Meta-data and Filters
- 2.7.
ServiceInvoker
- 3. JBoss
ESB 例子
-
- 3.1.
安装和运行JBossESB sample
- 3.2.
分析helloworld_action例子
- 3.3.
客户端调用服务
- 3.4.
小结
- 4. JBoss
ESB 部署和集成模块
-
- 4.1.
JBoss ESB Deployer
- 4.2.
JBoss ESB Service 集成
- 5. 总结
- Bibliography
Abstract
这篇文章主要是介绍了JBoss ESB的基础架构,以及通过一个简单的例子来认识JBoss ESB.
1. 总述
JBossESB是JBoss推出的ESB的实现,也是JBoss的SOA产品的基础.首先大家对于ESB的定义有很多的不同,我个人更喜欢把ESB看作是系统集成的一个平台.
JBossESB是一个基于消息的中间件(Message Oriented). 在这篇文章中,我们只是看待ESB中的一个很基础部份,也就是怎么从Endpoint
A发送信息给ESB的服务S1,然后再有S1发送信息到Endpoint B去调用服务. 至于其他的Router(路由)或者Data
Transformation(数据转换),这里赞不介绍.
我们就假设一个简单的系统集成场景来开始阐述JBossESB的设计和概念.
A系统(Endpoint A) – Message -> ESB -> – Message --> B系统 (Endpoint B) 所以,如果简单的对于JBossESB定义的话,我们可以定义以下三个概念:
- Message Listener (接收“inbound” message)
- Message Filter (发送 "outbound” message)
- Message (消息对象)
JBossESB 是一个面向服务(Service Oriented)的架构,所以在ESB内部的要么是一个Service,
要么是一个Message. 这里的Service就是指具有实现业务逻辑的服务,也可以是一个实现路由(Router),或者数据转化(Transformation)的服务.
就拿上面的这个例子,系统A发送一个Message给 ESB的一个服务,我们假设叫做S1, 那么S1收到Message后,做一些处理,转到S2的服务,S2再把处理后的结果发送给系统B.
这样就实现了A和B之间通过ESB的通信.
System A -> message -> S1 -> S2 ->.... -> message -> System B
那么在ESB内部是怎么去表达一个服务呢?这里引入了EndpointReference的概念,简称EPR.
有了服务之后,服务之间是通过什么样的传输层(比如JMS, FTP, HTTP)来通信呢? 所以ESB的内部也引入了Courier的API,
来统一抽象传输层. 刚我们也看到了,ESB的内部无非就是一系列的服务, 但是我们怎么来保存/注册这些服务的呢?
JBossESB是使用jUDDI来注册和保存这些服务元数据的.
2. JBossESB的概念
在要了解和运行JBossESB之前,我们最好了解下JBossESB中比较重要的几个概念
2.1. Message (消息)
ESB内部所交流/传递的都是消息,所以可见这个消息格式的重要性. 在JBossESB中, 定义了一个Message的对象,它是有以下几个部分构成的.
- Header (用来存放From, To, Reply-to等Addressing的信息).
- Body (存放信息主体)
- Attachment (用来存放附件等)
- Properties
- Context (主要是存放一些类似事务的信息等)
目前在Body里面一般来说存放两种格式的数据,一个是串行化数据(Serialized Object ),另外一个是XML文件,比如常见
的SOAP的payload. 在ESB中,还有两个定义,一个叫ESB-aware Message, 我们上面所讲的Message就是ESB-aware
Message, 正如名字说讲的,它是属于ESB内部的Message对象. 还有个叫 ESB unaware
Message,也就是说他同样也是一个message,比如SOAP Message,但是如果把soap
message直接让ESB来处理,是处理不了的,所以呢? 经常的在Listener 监听的端口会有个Adapter
(在JBossESB里叫做Gateway)来负责把ESB-unaware message 转成 ESB-aware
message.
2.2. Service (服务)
ESB的内部服务是用EPR来映射的. ESB的内部服务可以是任何的一个服务,比如说一个FTP的服务,一个基于文件系统的服务等等,
那么这个时候我们就需要用EPR来对这个服务进行描述.在EPR这个类里,主要是描述了这个服务的URI,以及所必须的一些元数据.
目前在JBossESB中提供的EPR有: FileEPR,EmailEPR,FTPEPR, HibernateEPR等等.
我们在注册服务的时候,是将EPR的信息注册到UDDI的容器里, 但不仅仅是EPR, 还有一些辅助信息,比如定义服务的category-name,
service-name. 这些将在后面继续介绍.
2.3. Listeners, Gateway
Listener的作用是负责监听端口,一般来说,客户端是发送消息到Listener,然后有Listener把消息传递给ESB,
我们可以把Listener看做是inbound router. 在JBossESB中,我们是叫GatewayListener,
它一般来说做两件事情.
- 监听Message.
- ESB-unaware message和 ESB-aware message的互转.
目前ESB支持的Gateway有: JMSGatewayListener, JBossRemotingGatewayListener,
FileGatewayListener等等. 在MessageComposer这个类里的compose/decompose方法来负责ESB-unaware信息和ESB-aware信息的转化.
public interface MessageComposer<T> {
/**
* Set the composer's configuration
*/
public void setConfiguration(ConfigTree config) throws ConfigurationException;
/**
* Compose an ESB "aware" message from the supplied message payload.
* Implementations need to construct and populate an ESB Message from the
* messagePayload instance.
*/
public Message compose(T messagePayload) throws MessageDeliverException;
/**
* Decompose an ESB "aware" message, extracting and returning the message payload.
*/
public Object decompose(Message message, T originalInputMessagePayload) throws MessageDeliverException;
}
2.4. Couriers
Courier的作用就是负责传输,正如以下接口所显示:
public interface Courier extends DeliverOnlyCourier
{
public boolean deliver(Message message) throwsCourierException,MalformedEPRException;
}
目前实现的Transport有:JmsCourier,
InVMCourier, HibernateCourier, FileCourier等传输层,在ESB内部是通过EPR来跟Courier进行关联的.
2.5. Actions
在JBossESB中,我们可以把ActionProcessingPipeline类看作是Message
Filter, 每个Message都会经过 ActionProcessingPipeline的处理. 里面有这么个方法:
public boolean process(final Message message)
而actionProcessingPipeline又是由Action
(ActionPipelineProcessor)来组成的. 我们可以把Action看成是Interceptor,
由它来实现具体的业务逻辑,可以是路由,又或者数据转化功能等等. 如果你用JBossESB的话,那么Action是一个非常重要的部分,我们来看下它所定义的接口.
public interface ActionPipelineProcessor extends ActionLifecycle
{
public Message process(final Message message) throws ActionProcessingException ;
public void processException(final Message message, final Throwable th) ;
public void processSuccess(final Message message) ;
}
一般来说自定义的Action把具体的业务逻辑放在Process的方法里,当然了,你也可以定义相对应的Exception处理方法,通过实现processException.在ESB代码中,自定义的Action可以用继承AbstractActionPipelineProcessor
或者 AbstractActionLifecycle.
2.6. Meta-data and Filters
在有些情况下,你需要一些全局的Interceptor,我们之前说的Action,可以理解成是每个service的interceptor,但是如果我需要使用log来记录一个消息在各个service之间传输的日志,
又或者想记录消息进入某个service的时间和退出的时间. 那么在JBoss ESB中就有Filter的概念.
如果你要实现自己的Filter,需要继承InputOputFilter类.
public class InputOutputFilter
{
/**
* Called as the message flows towards the transport.
*/
public Message onOutput (Message msg, Map<String, Object> params) throws CourierException
{
return msg;
}
/**
* Called immediately after the message is received from the transport.
*/
public Message onInput (Message msg, Map<String, Object> params) throws CourierException
{
return msg;
}
}
Tip
写完自己的Filter后,你需要在$JBossESB/server/config (e.g. default)/deploy/jbossesb.sar/jbossesb-properties.xml里面增加filter.
需要注意的是,在这里配置的filter是对所有的esb包都起作用,是个全局的变量.
onInput方法总是在从传输层获取到Message后,第一步所做的工作;类似的,
onOutput是给传输层传递前所做的最后一步工作.
你可以在TwoWayCourierImpl中看到这段代码的调用.
2.7. ServiceInvoker
对于客户端调用来说,EPR, Courier等都太底层了.所以如果对此进行了封装. 我们对每个service加以service-category和service-name的属性.
所以如果你想发送一个ESB的内部Message,你只需要知道目标service的service-category和service-name,然后就可以调用ServiceInvoker来调用服务.
不需要去使用Courier等底层的API, 另外用ServiceInvoker还可以支持fail-over等特性.
public class ServiceInvoker {
public ServiceInvoker(String serviceCategory, String serviceName) throws MessageDeliverException {
this(new Service(serviceCategory, serviceName));
}
public Message deliverSync(Message message, long timeoutMillis) throws MessageDeliverException, RegistryException, FaultMessageException
public void deliverAsync(Message message) throws MessageDeliverException
}
3. JBoss ESB 例子
为了更好的来解释JBossESB, 最好的一个方法就是试下JBossESB自带的例子,这里我们先以helloworld_action的例子来讲解.
3.1. 安装和运行JBossESB sample
到这里,你已经成功的运行了helloworld_action的例子.
- 从JBossESB网站下载
jbossesb-server-4.4.GA.zip
- 解压jbossesb-server-4.4.GA.zip, 假设到/var/local/jbossesb-sever4.4.
下面以$jbossesb来替代.
- 在$jbossesb中,运行 bin/run.sh 来启动ESB server
- 另外打开一个窗口,到$jbossesb/samples/quickstarts/helloworld_actions,
运行:
ant deploy
- 再运行:
ant runtest
- 回到JBoss ESB server的控制台上,应该可以看到以下的输出:
INFO [STDOUT] [Hello World Action].
INFO [STDOUT]
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
INFO [STDOUT] Body: Hello World Action
INFO [STDOUT] &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
INFO [STDOUT] ConsoleNotifier 2008/09/26 06:35:39.643<
BEFORE**
Hello World Action
AFTER**
>
3.2. 分析helloworld_action例子
下面我们来具体看下helloworld_action这个例子. 在JBoss ESB 4.4的例子中,我们默认以JBoss
Messaging来作为JMS的实现.
3.2.1. jboss-esb.xml
在看jboss-esb.xml的配置时候,我们应该分成两个部份. providers 和services .
3.2.1.1. providers
首先是<providers> ,它是有一系列的<provider> 组成,
目前有jms-provider, fs-provider, ftp-provider等等. 然后我们在provider里面定义这个.esb文件里面service所定义的listener所需要的bus,
Bus可以简单理解成消息传送所需要的传输层. 正如以下所显示的,我们定义了两个Bus,一个是给Gateway的Listener用,另外一个是给ESB-aware
Message传输所需要的传输层.
<providers>
<jms-provider name="JBossMQ" connection-factory="ConnectionFactory">
<jms-bus busid="quickstartGwChannel">
<jms-message-filter
dest-type="QUEUE"
dest-name="queue/quickstart_helloworld_action_Request"
/>
</jms-bus>
<jms-bus busid="quickstartEsbChannel">
<jms-message-filter
dest-type="QUEUE"
dest-name="queue/B"
/>
</jms-bus>
</jms-provider>
</providers>
Tip
虽然在这边写的是JBossMQ,
但是对于JBoss ESB server来说,是默认使用JBoss Messaging的; 如果是把JBoss
ESB安装在JBoss AS 4.x的服务器, 那么就是用JBoss MQ, 因为JBoss AS
4.x默认是使用JBoss MQ.
3.2.1.2. services
第二部份就是定义services的部份, 在这里定义了当前这个esb包所提供的services. 每个service又是由
<listener> 和 <actions> 组成的.
<services>
<service category="HelloWorld_ActionESB"
name="SimpleListener"
description="Hello World" >
<listeners>
<jms-listener name="JMS-Gateway"
busidref="quickstartGwChannel"
is-gateway="true"
/>
<jms-listener name="JMS-ESBListener"
busidref="quickstartEsbChannel"
/>
</listeners>
<actions mep="OneWay">
<action name="action2"
class="org.jboss.soa.esb.actions.SystemPrintln"
/>
<action name="displayAction"
class="org.jboss.soa.esb.samples.quickstart.helloworldaction.MyJMSListenerAction"
process="displayMessage">
<property name="exceptionMethod" value="exceptionHandler"/>
</action>
<action name="playAction"
class="org.jboss.soa.esb.samples.quickstart.helloworldaction.MyJMSListenerAction"
process="playWithMessage">
<property name="exceptionMethod" value="exceptionHandler"/>
</action>
</actions>
</service>
</services>
Warning
在listener里,我们通过 busidref来关联到我们定义在provider里面的bus.
在这里,我们定义了两个listener. 其中一个是做为Gateway,只负责从外界获取到JMS的消息,然后转成ESB内部所需要的Message.
而另外一个listener是用来这个Message在services内部之间通讯的通道. 所以对于每个service来说,一定要至少定义一个listener来作为内部Message传输用.
这里的action是对消息(Message)处理的地方.
3.2.2. MyJMSListenerAction
正如我们在上面看到的,我们在jboss-esb.xml中定义了action,我们看下MyJMSListenerAction.
public class MyJMSListenerAction extends AbstractActionLifecycle
{
protected ConfigTree _config;
public MyJMSListenerAction(ConfigTree config) { _config = config; }
public Message playWithMessage(Message message) throws Exception {
Body msgBody = message.getBody();
String contents = msgBody.get().toString();
StringBuffer sb = new StringBuffer();
sb.append("\nBEFORE**\n");
sb.append(contents);
sb.append("\nAFTER**\n");
msgBody.add(sb.toString());
return message;
}
}
Tip
我们只是截取其中的一部分来说明,一般来说每个Action都要继承AbstractActionLifecycle类,然后输入/输出参数都必须是ESB的Message.
方法名可以随便定义. 你只需要在jboss-esb.xml的action的process属性中写相对应的方法名就可以.
如果不写,默认是process方法.
这里的ConfigTree是个很重要的属性,我们很经常的会在Action配置其他的信息,那么
所有的信息都可以通过ConfigTree来获取到.比如说在某个Action中配置静态路由信息等等.也正是由于Action中你可以随意的配置你自己的信息,增加了很多的灵活性和扩展性.
3.2.3. esb文件目录结构
我们先看下部署在server下的.esb包的文件目录,一般是包括以下些东西.
- /META-INF/jboss-esb.xml
- /META-INF/deployment.xml 在这里定义对其他包或者服务的依赖,或者配置classloader.
- jbm-queue-service.xml (optional) 这里是定义启动所需要的Queue
- **.jar (optional) 放些所需要的第三方包
- 所需要的些classes文件
3.3. 客户端调用服务
目前在JBossESB中,一般有两种方式来调用service. 一种是通过Gateway listener,
另外一种是直接通过ServiceInvoker的API来调用.
3.3.1. 通过Gateway来调用服务
回到我们的例子,我们通过JMS Gateway来访问ESB的服务.
public class SendJMSMessage {
public void setupConnection() throws JMSException, NamingException
{
InitialContext iniCtx = new InitialContext();
Object tmp = iniCtx.lookup("ConnectionFactory");
QueueConnectionFactory qcf = (QueueConnectionFactory) tmp;
conn = qcf.createQueueConnection();
que = (Queue) iniCtx.lookup("queue/quickstart_helloworld_action_Request");
session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
conn.start();
System.out.println("Connection Started");
}
public void sendAMessage(String msg) throws JMSException {
QueueSender send = session.createSender(que);
ObjectMessage tm = session.createObjectMessage(msg);
tm.setStringProperty(StoreMessageToFile.PROPERTY_JBESB_FILENAME, "HelloWorldActionTest.log");
send.send(tm);
send.close();
}
public static void main(String args[]) throws Exception
{
SendJMSMessage sm = new SendJMSMessage();
sm.setupConnection();
sm.sendAMessage(args[0]);
sm.stop();
}
}
应该说,这是一个很普通发送JMS消息送到一个指定的Queue.
注意,我这里并没有全部拷贝这个SendJMSMessage类.
只是拷贝出重要的部分.(如果想要看完整的,请参考helloworld_action例子下面的代码)
3.3.2. 利用ServiceInvoker直接发送ESB
Message
在helloworld_action例子中,没有直接SendESBMessage的客户端来调用,但是我们可以看下helloworld的sample下面的,因为是一样的.
public class SendEsbMessage
{
public static void main(String args[]) throws Exception
{
// Setting the ConnectionFactory such that it will use scout
System.setProperty("javax.xml.registry.ConnectionFactoryClass","org.apache.ws.scout.registry.ConnectionFactoryImpl");
if (args.length < 3)
{
System.out.println("Usage SendEsbMessage <category> <name> <text to send>");
}
Message esbMessage = MessageFactory.getInstance().getMessage();
esbMessage.getBody().add(args[2]);
new ServiceInvoker(args[0], args[1]).deliverAsync(esbMessage);
}
}
正如我们之前所说的,客户端用ServiceInvokerAPI大大简化了调用服务的过程.
我们在jboss-esb.xml中看到每个service都会有service-category和service-name的属性.
在ServiceInvoker中,用户只需要提供这两个属性,就能调用到ESB的服务,当然了,还需要juddi.properties文件.
这也是为什么我们的 sample下面一般会有这个文件.
ServiceInvoker的使用
ServiceInvoker是个相当重要的API,应该说在ESB service之间服务的互相调用,就是用ServiceInvoker来完成的.
因为ServiceInvoker对Courier等进行了一层的抽象封装. 所以用ServiceInvoker来调用服务,是可以支持fail-over等高级特性的.
3.4. 小结
我们结合之前的概念,来看下这个例子的调用过程. 这里我们假设是通过JMS Gateway来调用ESB服务的.
- 从JMS Gateway listener接收到JMS
Message.然后把JMS message 转成 ESB Message.
- 使用ServiceInvoker
API发送 ESB Message到指定的service.
- ESBAwareListener接收到ESB
Mesage后,找到对应的service,把Message提交给ActionProcessingPipeline来处理.
我们这里讲述了一个简单的调用oneway服务的一个过程.
4. JBoss ESB 部署和集成模块
通过前两个部分的介绍,应该说JBoss ESB简单的内部框架应该比较清楚了. 但是我们还没有涉及到其他部署和集成的一些模块.
正因为通过比如对Smooks, JBoss Rules, jBPM等的集成,使得JBoss ESB的功能更加强大,好用.
4.1. JBoss ESB Deployer
因为JBoss ESB 4.系列是基于JBoss AS的,所以有个扩展类JBoss4ESBDeployer类是来负责对JBoss
ESB包的监听和解析. 我们可以看到在$jbossesb.sar/META-INF/jboss-service.xml里面定义了JBoss4ESBDeployer的服务.
<mbean code="org.jboss.soa.esb.listeners.config.JBoss4ESBDeployer"
name="jboss.esb:service=ESBDeployer">
<depends>jboss.esb:service=ESBRegistry</depends>
<depends>jboss.esb:service=JuddiRMI</depends>
</mbean>
所以,通过部署了JBoss4ESBDeployer,凡是以.esb结尾的包,都是由JBoss4ESBDeployer来解析,
如果你对这块比较感兴趣,还可以看下以下的几个类.
org.jboss.soa.esb.listeners.config.ConfigurationController
org.jboss.soa.esb.listeners.config.JBoss4ESBDeployment
org.jboss.soa.esb.listeners.config.Generator
4.2. JBoss
ESB Service 集成
正如我们之前所看到的,因为JBossESB内部本身的架构比较灵活,所以说你只要写个自己的Action,可以很方面的做和其他library的集成.
直到JBoss ESB 4.4.GA版本,有以下一系列的集成.
- Smooks的集成. (主要是负责数据转化等的服务).
- JBoss Rules的集成. (可以使用Rules来定义你的路由,这里默认提供了Content-based
Router)的功能.
- jBPM的集成. (可以提供访问jBPM提供的工作流服务)
- Soap-UI的集成. (负责跟webservice打交道)
- Spring的集成. (访问Spring提供的服务)
- slsb (访问无状态SessionBean提供的服务)
- wise的集成. (另外一种方式通过跟web service打交道)
5. 总结
应该说到目前,我们只是讨论了JBoss ESB一个小的简单的模块,JBoss ESB本身还有更多的功能,比如之前所说的集成模块等等.
所以,如果你想比较完整的了解JBoss ESB,应该是去JBoss ESB的站点,然后去读文档,这里特别推荐<JBoss
Programmers Guide > . 除此之外, JBoss ESB本身还带有大量的例子.可以通过运行这些例子,更容易的了解JBoss
ESB. 如果在用JBoss ESB遇到问题是,可以上JBoss
ESB论坛提问.
Bibliography
-
JBoss ESB Programmers Guide (强烈推荐)
-
JBoss ESB website
-
JBoss ESB Blog
-
JBoss ESB documentation
-
JBoss ESB user forum
|