IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  WebSphere  >

使用 CBE 和 JAXB 集成实现对 WebSphere Application Server 上应用程序的业务数据监控

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 初级

程 国强, 软件工程师, IBM
李 栋, 软件工程师, IBM CDL

2009 年 10 月 26 日

当今企业提倡的是随需而变,因此需要在尽可能短的时间分析元数据来响应这些变化的巨大挑战。此外,如何提前预知企业应用中的潜在问题,如客户注册量、订单量的浮动等等,都对企业制定计划至关重要,因此我们需要对元数据进行及时的收集分析。 EJB3 作为当前的主流技术之一,提供了一系列的技术点来支持开发人员更好的操作 JavaBean,使得对 JavaBean 的控制更加简单,同时,JAXB 提供了对 JavaBean 与 XML 之间的相互转换。

如我们所知,所有在 WebSphere Application Server 的应用都是基于 JavaBean 来进行数据的交互和传递,因此我们考虑结合使用 EJB3 的新技术点和 JAXB 来实现一种通用的技术,利用 JAXB 来实现对 EJB3 应用程序的配置,以达到监控 JavaBean 中业务数据的目的。

本文将通过对一个实例的讲解及样例代码的分析,对 JAXB,CBE,EJB3 做简要介绍,然后通过实例介绍了结合使用以上几种技术实现了对企业元数据的监控,并为以后进行数据分析提供了前提。

JAXB 技术简介

JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据 XML Schema 产生 Java 类的技术。该过程中,JAXB 也提供了将 XML 实例文档反向生成 JavaBean 的方法,并能将 JavaBean 的内容重新写到 XML 实例文档。从另一方面来讲,JAXB 提供了快速而简便的方法将 XML 模式绑定到 Java 表示,从而使得 Java 开发者在 Java 应用程序中能方便地结合 XML 数据和处理函数。

本文用到了 JAXB 中将 JavaBean 通过 Annotation 序列化成 XML 的功能,JAXB 有如下重要的 Annotation,对于每个 Annotation 在后文中将做详细介绍。

@XmlAccessorType

@XmlAttribute

@XmlElement

@XmlElements

@XmlRootElement

@XmlTransient

@XmlValue

下图给出了 JAXB 的体系架构:

如图所示,JAXB 通过“绑定编译器”(Binding Compiler) 简化了 Java 程序对 XML 文档的访问,然后将 XML 文档以 JavaBean 的格式展现出来。

同时,JAXB 提供相应的 API 实现了对 XML 文档与树形内容对象之间的相互转换,该树形对象内容相比于 XML 文档来说更有利于节省内存使用。


图 1. JAXB 体系架构图
图 1. JAXB 体系架构图




回页首


公共基础事件简介

公共事件基础架构(Common Event Infrastructure)是 WebSphere 的重要组成部分,简称 CEI。它由一组事件处理模块组成,负责创建、存储、分发事件。之所以叫公共事件,是因为 CEI 帮助应用程序创建出基于公共 XML 格式的扩展性强的通用基础事件,使得不同应用程序生成的不同类型的事件含有统一的标准信息。因此,我们把这些 XML 格式的对象称为公共基础事件通过(Common Base Events,CBE)定义了统一的格式。我们把一个信息的产生和变化定义为一个事件,CEI 记录了事件,从而记录了各种系统信息和业务信息的发生和变化。用户使用 CEI,便能轻松地获知重要事件的发生。

下图给出了 CEI 的架构:


图 2. CEI 体系
图 2. CEI 体系




回页首


JAXB 和 CBE 的结合使用

如上所述,一方面,JAXB 支持 JavaBean 到 XML 的转换,而在 WebSphere Application Server 环境下,所有的 EJB3 应用程序都基于 JavaBean。另一方面,在现有的 EJB3 应用程序中,CBE 只包含了相应事件的时间点信息,例如动作开始,动作结束等,它不包含业务流程中的数据信息,可以说没有实际的业务含义。因此我们可以考虑通过在 EJB3 应用程序中嵌入 JAXB 来生成符合 XML 规范的,并且记录了各种系统信息和业务信息的 CBE,使得用户在后期可以通过对 CBE 中的有效数据进行分析来不断优化业务流程,例如可以通过 WebSphere Business Monitor 来对这些 CBE 进行来进行分析和整理。





回页首


EJB3 应用程序的新变化

  • 编程模型的简化

EJB3 完全基于 Annotation 之上,各种 Annotation 用于修饰各种 Java 对象、成员变量,从而免去了开发人员通过配置文件来进行繁琐的设置。

  • 拦截器概念

面向切面编程(Aspect Oriented Programming, AOP):是 OOP 的延续,是 Aspect Oriented Programming 的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

EJB3 引入了类似 AOP 中的拦截器概念,拦截器可以拦截会话状态 Bean 和消息驱动 Bean 的方法调用或者生命周期事件,它用于封装应用的公用行为,使得这些行为和业务逻辑分离,当公用行为发生变化,就不必修改相因的业务类,这样就能简化程序的维护。拦截器可以是同一 Bean 类中的方法或者自定义的一个外部类。

EJB3 提供了 @Interceptors 来定义一个或多个在外部类的拦截器。我们将会在后文结合实例来介绍拦截器的用法。





回页首


利用 JAXB Annotation 为 EJB3 应用程序添加修饰符

如我们所知,EJB3 应用程序有如下几个重要部分组成:

    1. 体现具体业务数据的 JavaBean;
    2. 会话 Bean 接口及相应实现;
    3. 消息驱动 Bean;

为了实现对 EJB3 应用程序的业务数据的拦截,我们需要对以上三个部分分别添加 JAXB Annotation 修饰符,将 JavaBean 经过序列化操作转换为符合 CBE 格式的 XML,并能够将 XML 文件发送到指定的服务器队列中。

对已有 EJB3 应用程序进行的改动设计主要分为以下几个重要步骤:

    1. 为 JavaBean 增加 JAXB Annotation 修饰符;
    2. 为消息驱动 Bean 的 onMessage/onNotification 发放增加 JAXB Annotation 修饰符;
    3. 定义拦截器;
    4. 在拦截器中定义 CBE;

另外,为方便理解,本文假定为以下业务场景:

    1. 客户通过注册系统注册账号;
    2. 客户通过客户端提交注册信息;
    3. 客户端更新数据库,同时将注册信息以 CBE 发送到指定队列。

为 JavaBean 增加 JAXB Annotation 修饰符

EJB3 应用程序中的 JavaBean 定义了业务过程中所需的元数据格式,其中包含了一些必要的数据属性,而我们为了能够通过 CBE 来查看具体的业务数据,我们就要为这些数据属性添加 JAXB Annotation 来进行序列化修饰,为此,我们可以用 JAXB 提供了的修饰符来指定这个 JavaBean。

主要步骤如下:

  • 为元数据父亲节点增加 Annotation 修饰:在将实体 JavaBean 序列化到 XML 的过程中,XML 根节点所对应的就是所有企业元数据中的父亲节点,JAXB 提供了 @XmlRootElement 修饰符,可以通过 name,namespace 两个属性来定义 XML 根节点的名字以及命名空间。
  • 为数据属性增加 Annotation 修饰:在将 JavaBean 序列化到 XML 的过程中,XML 中的每个属性节点对应的就是所有企业元数据中的属性,JAXB 提供了 @XmlTransient 和 @XmlElement 两个修饰符供用户选择来决定是否需要对相应属性进行序列化操作。@XmlTransient 标示此属性在序列化过程中不需要进行转化,忽略此属性;@XmlElement 标示此属性在序列化过程中需要进行转换提取相应数据信息,通过 name,namespace 两个属性来定义 XML 属性节点的名字以及命名空间;

清单 1. JavaBean Annotation 修饰
 @XmlRootElement(name = "WbiCustomerCi", 
 namespace = 
"http://www.ibm.com/xmlns/prod/websphere/j2ca/peoplesoft/wbicustomerci") 
 public class WbiCustomerCi implements Record, BeanMetadata, RecordHolder, 
 Serializable { 

 @XmlTransient// 此属性无需序列化操作
 public static LinkedHashMap propertyAnnotations = new LinkedHashMap(); 

 @XmlTransient// 此属性无需序列化操作
 public static LinkedHashMap objectAnnotations = new LinkedHashMap(); 

 @XmlTransient// 此属性无需序列化操作
 private HashSet _setAttributes = new HashSet(); 

 @XmlElement(namespace = 
"http://www.ibm.com/xmlns/prod/websphere/j2ca/peoplesoft/wbicustomerci") 
 private java.math.BigInteger Customerid; 

 @XmlElement(namespace = 
"http://www.ibm.com/xmlns/prod/websphere/j2ca/peoplesoft/wbicustomerci") 
 // 此属性需序列化操作
 private String Customerfirstname; 

 @XmlElement(namespace = 
"http://www.ibm.com/xmlns/prod/websphere/j2ca/peoplesoft/wbicustomerci") 
 // 此属性需序列化操作
 private String Customerlastname; 

 @XmlElement(namespace = 
"http://www.ibm.com/xmlns/prod/websphere/j2ca/peoplesoft/wbicustomerci") 
 // 此属性需序列化操作
 private Wbiaddress[] Wbiaddress; 

为消息驱动 Bean 的 onMessage/onNotification 方法增加 JAXB Annotation 修饰符

在 EJB3 应用程序中,元数据通过消息驱动 Bean 中的 onMessage/onNotication 方法进行处理,然后发送给会话 Bean,因此,为了对元数据进行截获,我们利用 EJB 3 提供的 @Interceptors 修饰符对 onMessage、onNotification 进行定义,其中的属性值指定的为拦截器类,在该类中将实现对企业元数据的分析,同时进行构造 CBE 以及发送 CBE 的操作。拦截器类中的所有操作和 onMessage/onNotification 是并行的,不会更改元数据。


清单 2. onMessage 拦截器修饰
 @Interceptors( { MetadataInterceptor.class }) 
 public onMessage(arg0, arg1) 
 { 
 try { 
…
 } catch (Exception e) { 
 throw new javax.ejb.EJBException(e); 
 } 
 }

拦截器定义

如上所述,我们已经为 onMessage、onNotification 方法添加拦截器定义后,接着我们就需要实现相应的拦截器 MetadataInterceptor。


清单 3. 拦截器 MetadataInterceptor
 public class MetadataInterceptor { 

 @AroundInvoke 
 public Object monitorEvents(javax.interceptor.InvocationContext invokeCtx) 
 throws Exception { 
 constructCBE(invokeCtx); 
		
 } 
 return invokeCtx.proceed(); 
 }

@AroundInvoke 修饰符指定了要用作拦截器的方法 monitorEvents(invokeCtx),拦截器方法与被拦截的业务方法执行在同一个 java 调用堆栈、同一个事务和上下文中,这样就保证了整个事务的全局完整性。另外,在拦截器中,我们调用了 invokeCtx.proceed() 方法,这个方法会保证调用所有的拦截器的 @AroundInvoke 方法,直到后面的拦截器全部执行结束,EJB 容器才会执行被拦截的业务方法,本文中只使用了一个拦截器,所以执行完后就直接返回。值得注意的是 invokeCtx.proceed()方法必须在拦截器代码中被调用,否则被拦截的业务方法就不会被执行到,因此为了保证不影响原有的业务逻辑,我们在拦截器中必须使用此方法。

在拦截器中定义 CBE

在上面的拦截器中,我们通过 constructCBE(invokeCtx) 来实现构造并发送 CBE 的流程。

构造 CBE 消息文件头: 公共基础事件有其特定的格式,所有在拦截器类中,我们需要通过设定其相关属性以满足其式需求,其中包括版本信息,创建时间信息,位置信息,环境变量信息,时间戳信息,线程 ID,事件源组件信息。清单 4 给出了构造定义 CBE Header 的样例代码。


清单 4. 构造 CBE 消息文件头
 // 创建事件工厂
 org.eclipse.hyades.logging.events.cbe.EventFactory eventFactory = null; 
 eventFactory = (org.eclipse.hyades.logging.events.cbe.EventFactory) 
 org.eclipse.hyades.logging.events.cbe.EventFactoryFactory.createEventFactory(); 

 // 创建事件
 org.eclipse.hyades.logging.events.cbe.CommonBaseEvent event = null; 
 String eventXMLString = null; 
 event = eventFactory.createCommonBaseEvent(); 

 // 定义事件版本信息
 event.setVersion("1.0.1"); 
 long currentTime = System.currentTimeMillis(); 

 // 定义事件创建时间
 event.setCreationTimeAsLong(currentTime); 

 // 定义事件全局 ID 
 String timeStamp = Long.toString(currentTime); 
 String threadID = Long.toString(Thread.currentThread().getId()); 
 event.setGlobalInstanceId(threadID + "_" + timeStamp); 

 // 定义组件
 org.eclipse.hyades.logging.events.cbe.ComponentIdentification componentID = 
 eventFactory.createComponentIdentification(); 
 componentID.setComponentIdType 
 (org.eclipse.hyades.logging.events.cbe.ComponentIdentification. 
 COMPONENT_ID_TYPE_PRODUCT_NAME); 
 componentID.setComponent("IBM WebSphere"); 
 componentID.setComponentType("WebSphere"); 
 componentID.setSubComponent(boSchemaName); 
 componentID.setLocationType(org.eclipse.hyades.logging.events. 
 cbe.ComponentIdentification.LOCATION_TYPE_HOSTNAME); 
 componentID.setLocation("localhost"); 
 componentID.setExecutionEnvironment 
 (System.getProperty("os.name")); 
 componentID.setProcessId(timeStamp); 
 componentID.setThreadId(threadID); 
 event.setSourceComponentId(componentID); 

 // 定义事件环境
 org.eclipse.hyades.logging.events.cbe.Situation situation = 
 eventFactory.createSituation(); 
 situation.setRequestSituation("EXTERNAL", "", "SUCCESSFUL"); 
 event.setSituation(situation); 

构造 CBE 消息正文: 定制好 CBE Header 后,我们需要将截获的企业元数据信息通过 JAXB 的 Marshller 接口进行序列化转化,作为整个 CBE 的 Payload 嵌入到 XML Body 之中。清单 5 给出了构造定义 CBE 消息正文的样例代码。


清单 5. 构造 CBE 消息正文
 // 定义输出流,并与元数据进行绑定
 java.io.ByteArrayOutputStream outputStream = 
 new java.io.ByteArrayOutputStream(); 
 Class recordClass = Class.forName(recordName); 
 javax.xml.bind.JAXBContext jaxbCtx = 
 javax.xml.bind.JAXBContext.newInstance(recordClass); 
 javax.xml.bind.Marshaller marshaller = 
 jaxbCtx.createMarshaller(); 
 marshaller.setProperty( 
 javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE); 
 marshaller.setProperty( 
 javax.xml.bind.Marshaller.JAXB_ENCODING, 	"UTF-8"); 

 // 转换元数据并加载到输出流 
 marshaller.marshal(businessObject, outputStream); 
 String xml = outputStream.toString(); 

 // 将 event(header, 元数据)转换为标准 XML 
 event.addAny(xml.substring(xml.indexOf(">") + 1)); 
 eventXMLString = org.eclipse.hyades.logging.events.cbe. 
 util.EventFormatter.toCanonicalXMLDocString(event); 

发送 CBE: 将 CBE 发送到指定的应用服务器上,我们需要事先定义好相应的队列连接工厂以及相应的队列,然后通过 JMS 进行数据的发送。清单 6 给出了发送 CBE 的样例代码。


清单 6. 发送 CBE
 javax.naming.InitialContext initContext = 
 new javax.naming.InitialContext(); 
 // 通过 JNDI 查询事件队列连接工厂
 javax.jms.ConnectionFactory connectionFactory = 
 (javax.jms.ConnectionFactory) initContext.lookup( 
 "jms/cei/EventQueueConnectionFactory"); 
 javax.jms.Destination destination = (javax.jms.Destination) 

 // 通过 JNDI 查询事件队列
 initContext.lookup("jms/cei/EventQueue"); 
 initContext.close(); 

 // 创建连接
 javax.jms.Connection connection = 
 (javax.jms.Connection) connectionFactory.createConnection(); 
 javax.jms.Session session = connection.createSession( 
 true,javax.jms.Session.SESSION_TRANSACTED); 

 // 设置 JMS 消息内容
 javax.jms.MessageProducer sender = session.createProducer(destination); 
 javax.jms.TextMessage message = session.createTextMessage(); 

 // 加载 CBE 
 message.setText(eventXMLString); 
 message.setJMSType("CREATE_EVENTS_NOTIFICATION_V2_0"); 
 message.setStringProperty("eventName", operationName); 
 sender.send(message); 

 // 释放连接
 sender.close(); 
 session.close(); 
 connection.close(); 





回页首


在 Websphere Application Server 上查看 EJB3 应用程序的 CBE 事件

通过以上章节的介绍,我们实现了利用 JAXB 对现有 EJB3 应用程序的改造设计,接下来我们通过一个简单的操作来验证程序的可用性。

  1. 启动 WebSphere Application Server。
  2. 通过 WebSphere Application Server 中的 Admin Console 配置队列信息

连接工厂: jms/cei/EventQueueConnectionFactory,队列名:jms/cei/EventQueue


图 3. 配置连接工厂
图 3. 配置连接工厂

图 4. 配置队列
图 4. 配置队列
  1. 部署应用程序
  2. 通过客户端发起 3 次注册请求
  3. EJB3 应用程序执行相应请求进行数据处理,并将截获的注册数据通过 CBE 格式发送到创建的队列当中,我们通过 Admin Console 对 CBE 事件进行查看。

通过 Runtime 的标签页,我们可以通过“Current message depth”来了解当前已经截获的 CBE 事件个数。


图 5. 查看 CBE 事件个数
图 5. 查看 CBE 事件个数

同时,通过点击 Messages 可以查看每个 CBE 事件的内容。


图 6. 利用 Admin Console 查看 CBE 事件内容
图 6. 利用 Admin Console 查看 CBE 事件内容

图 7. 利用浏览器查看 CBE 事件内容(查看大图
图 7. 利用浏览器查看 CBE 事件内容

通过对 CBE 内容的查看,我们可以实时了解客户的注册请求信息,另外,在以后的业务整合当中,我们也可以利用诸如 WebSphere Business Events 和 WebSphere Business Monitor 来对这些符合 CBE 规范的数据信息进行收集分析。





回页首


总结

本文通过对一个实例的讲解及样例代码的分析,对 JAXB,CBE,EJB3 做了简要介绍,然后通过实例介绍了结合使用以上几种技术实现了对企业元数据的监控,并为以后进行数据分析提供了前提。






回页首


下载

描述名字大小下载方法
本文代码示例Sample_JavaBean_Inteceptor_Class.zip2 KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术


作者简介

程国强,IBM 中国软件开发中心软件工程师。自 2006 年任职于 WebSphere Adapters 团队,从事相关产品的研发工作,对 Connectivity,SOA,Web2.0 等领域有兴趣。


李栋,IBM 中国软件开发中心高级软件工程师。自 2004 年任职于 WebSphere Adapters 团队,从事相关产品的设计,研发及测试工作,负责产品的架构设计。




对本文的评价








IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款