Smooks 结构化事件流处理

阅读数:4180 2009 年 1 月 14 日 03:12

概览

Smooks 是一个开源的 Java 框架,用于处理“数据事件流”。它常常被认为是一个转换框架并以此被用于好几个产品和项目中,包括 JBoss ESB (以及其它 ESB)。然而究其核心,Smooks 未提及“转换”或者其它相关的词汇。它的应用远不只这一点!

Smooks 的工作是将结构化 / 层次化的数据流转变成“事件”流,然后交与“访问者逻辑(Visitor Logic)”进行分析,生成结果(可选的)。

源 -> 结构化事件流(访问者逻辑) -> 结果

那么,有哪些工作是这个工具可以完成,而 sax 和 dom 等工具不能完成的呢?鉴于 Smooks 构建于这些技术之上,所以简单的回答是“没有”。 Smooks 真正的价值在于能更方便地消费 SAX 和 DOM( Smooks 现在还不支持基于 StAX 的过滤器)。它提供了一个访问者 API,以及一个配置模型,允许你轻易地将访问者逻辑的目标设定为具体的 SAX 事件(如果使用的是 SAX 过滤器)或 DOM 元素(如果使用的是 DOM 过滤器)。 Smooks 同时还以一种标准方式简化了对非 XML 源数据格式(EDI,CSV,JSON,Java 等等)的消费,即由数据源产生的标准事件流变成了所有这些不同源数据格式的事实上的规范形式。这正是 Smooks 工作的关键!

使用 Smooks 的方式有两种,你可以使用其中之一或结合使用它们:

  • 模式一:你可以完全投入到 Smooks 中,编写你自己的定制访问者逻辑事件处理器,将其用于处理一个数据源事件流中特定事件。使用这一模式,你必须熟悉核心的 API。
  • 模式二:你可以重用由 Smooks 发行版提供的开箱即用解决方案,其数目正在不断的增长中。在这种模式下,你只需要重用别人创建的组件即可,重新配置它们来处理你的数据源,例如,通过配置一些参数就可以由 EDI 数据源生成 Java 对象模型。

在这篇文章中,我们会快速地浏览一遍 Smooks v1.1 发行版提供的一些开箱即用的功能,即那些你不需要编写任何代码就可加以利用的功能(即模式二)。这包括:

  1. 多源数据格式:“轻易”地消费诸多流行的数据格式,包括 XML,EDI,CSV,JSON 和 Java(是的,你可以以一种声明性的方式完成 java 到 java 的转换)。
  2. 转换:利用诸多选项消费由数据源产生的事件流,产生其它格式的结果(即,对源进行“转换”)。这包括能在过滤源数据流时对 Smooks 所捕获的数据模型应用 FreeMarker 和 XSL 模板。鉴于这些模板资源能被运用于源数据事件流内部的事件,因此它们能被用来执行“基于片断的转换(fragment based transforms)”。这意味着 Smooks 能被用于对数据源的片断执行转换,而不仅限于将数据源作为一个整体来施行转换。
  3. Java 绑定:以一种标准方式由所支持的数据格式(即不仅限于 XML)创建和生成 Java 对象模型 / 图。由 EDI 数据源 生成某对象模型的配置与由 XML 数据源或 JSON 数据源生成对象模型所进行的配置几乎一模一样。唯一区别在于绑定配置的“事件选择器(event selector)”取值不同。
  4. 大型消息处理:Smooks 支持多种处理大型消息(GBs)的方式,它是通过基于 SAX 的过滤器完成的。由于综合了 基于片断转换、Java 绑定,以及使用节点模型混合 DOM 和 SAX 模型所带来的能力,Smooks 可以使用较低的内存空间处理大型消息。这些能力允许你执 行直接的 1 对 1 转换,同时也支持对大型消息数据流执行 1 对多的分解、转换和路由。
  5. 消息修饰:使用数据库数据修饰消息。这可以按片断来完成,即你可以按片断来管理在一个片断上的修饰。与此相关的一个用例是一个包含了消费者 ID 列表的消息在发往另一个流程前需要从数据库提取消费者地址和概要数据来丰富。
  6. 基于可扩展 XSD 的配置模型:从 Smooks v1.1 开始,你可以用自己的可重用定制访问者逻辑配置模型来扩展 Smooks XSD 配置名字空间。创建这些定制配置扩展只是一项简单的配置工作,这个工作极大的增进了这些重用组件的可用性。所有的现有 Smooks 预置组件都利用了这一工具。

处理不同数据格式

Smooks 的一个关键特性就是能很容易地将其配置成用标准方式处理不同数据格式。这意味着如果你为 Smooks 开发了一些定制的访问者逻辑,这些代码可以立即用于处理任何受支持的数据格式,就仿佛是 Smooks 的预置组件(Java 绑定等)一样。与之相关的,如果你为某种非预置支持的数据格式(例如 YAML)开发了一个定制的阅读器实现,你立即就具备了一个能力:使用所有可获得的预置访问者逻辑(例如,Java 绑定组件)来处理该类型数据产生的数据事件。使这成为可能的原因在于 Smooks 组件处理的是标准化的事件流(即规范形式)。

Smooks 提供对处理 XML、EDI、CSV、JSON 和 Java 对象的开箱即用的支持。默认情况下,Smooks 将源数据流当作 XML 来读取(除非另有配置)。一个例外是 Java 对象源,它将被自动识别。对于其它所有数据格式类型,在 Smooks 配置里必须配置一个“阅读器”。下面是一个 CSV 阅读器的配置实例:

<xml version="1.0"?>

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"

xmlns:csv
="http://www.milyn.org/xsd/smooks/csv-1.1.xsd">



<csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />



<smooks-resource-list>

EDI、JSON 等阅读器的配置方式类似,都是通过唯一的配置名字空间进行的,比如<edi:reader></edi:reader><json:reader></json:reader>等。这些名字空间式的配置通过前面所讲到的基于可扩展的 XSD 配置模型得以实现。

配置好的阅读器负责将源数据流翻译成结构化的数据事件流(即规范形式——目前基于 SAX2)。Smooks 监听这一事件流,在合适的时候触发配置好的访问者逻辑(例如模板或绑定资源)。

执行一个 Smooks 过滤器流程

这很简单:

private  Smooks  smooks = new  Smooks ("/smooks-configs/customer-csv.xml");public void transCustomerCSV(Reader csvSourceReader, Writer xmlResultWriter) {

smooks.filter(
new StreamSource(csvSourceReader), new
StreamResult(xmlResultWriter));

}

Smooks.filter()方法消费标准的 javax.xml.transform.Source javax.xml.transform.Result 类型。Smooks 项目同样定义了诸多新实现

可视化非 XML 的结构化数据事件流

XML 是最简单的“由源数据流产生事件流”的可视化方案。所以对于一个 XML 源而言,不存在真正的问题。而对非 XML 源(如 CSV),事情就不那么简单了。该源一般与 XML 毫无共通之处。为了解决这个问题,Smooks 提供了执行报告产生器(Execution Report Generator)工具。这一工具的一大用途就是帮助你对非 XML 数据源产生的事件流进行可视化,格式为 XML。它同样是很好的调试工具。

这一报告产生工具被注入到了 Smooks 的执行上下文中:

private Smooks smooks = new Smooks("/smooks-configs/customer-csv.xml");public void transCustomerCSV(Reader csvSourceReader, Writer xmlResultWriter) {    

ExecutionContext executionContext = smooks.createExecutionContext(); executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));

smooks.filter(new StreamSource(csvSourceReader), new StreamResult(xmlResultWriter), executionContext
);

}


(在 Smooks v1.1 中)其输出是如下的一个 HTML 页面:

JBoss 正在为 Smooks 开发一个 Eclipse 编辑器,它将作为 JBoss Tools 的一部分。这些工具将进一步简化可视化并操作非 XML 数据源事件流的过程。

拆分、转换与路由

这一用例很好地示范了如何组合几个 Smooks 功能来执行一个更复杂的任务。

继续 CSV 的例子,我们有以下的基本需求:

  1. CSV 流可能会很大,因此我们需要使用 SAX 过滤器。
  2. 我们需要将每个 CSV 记录路由到一个 JMS 端点,格式是 XML。这意味着我们需要拆分,转换和路由这些消息。

Smooks 使用诸如 XSL 和 FreeMarker 这样的流行模板技术来对使用基于片断的转换提供支持。Smooks 同时还提供了从源事件流(同样可能是非 XML 的)抓取 DOM 模型的能力,就算在使用 SAX 过滤器的情况下也能实现。有了这一功能,Smooks 能够从源数据片断中构建“迷你”DOM 模型并让其它 Smooks 资源(如 FreeMarker 模板和 Groovy 脚本资源)可以利用它们。采取这种方式,你能在保持以流式环境进行处理的同时,得到某些 DOM 处理模型的好处。对于此处所说的用例,我们将使用 FreeMarker 作为模板技术。

Smooks 同样对将数据片断(由源数据片断所产生)路由到多个不同的端点类型(即 JMS,文件或数据库)提供了开箱即用的支持。如同 Smooks 中的所有事物一样,这种能力总是可以被扩展或被其他用例重复使用,例如,可以轻而易举的插入一个定制的电子邮件访问者组件。 JBoss ESB (以及其它的 ESB)从运行于 ESB 之上的 Smooks 过滤流程内部提供定制的 Smooks 访问者组件来完成基于片断的 ESB 端点路由。

那么配置 Smooks 来完成上述用例就十分简单了:

<xml version="1.0"?>

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"

xmlns:csv
="http://www.milyn.org/xsd/smooks/csv-1.1.xsd"

xmlns:jms
="http://www.milyn.org/xsd/smooks/jms-routing-1.1.xsd"

xmlns:ftl
="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd"><params>

(1)
<param name="stream.filter.type">SAXparam>

params>

(2)

<csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />

(3)

<resource-config selector="csv-record">

<resource>org.milyn.delivery.DomModelCreatorresource>

resource-config>

(4)

<jms:router routeOnElement="csv-record" beanId="csv_record_as_xml" destination="xmlRecords.JMS.Queue" />

(5)

<ftl:freemarker applyOnElement="csv-record">

(5.a)
<ftl:template>/templates/csv_record_as_xml.ftlftl:template>

<ftl:use>

(5.b)
<ftl:bindTo id="csv_record_as_xml"/>

<ftl:use>

<ftl:freemarker><smooks-resource-list>
  1. 配置 (1) 指示 Smooks 使用 SAX 过滤器。
  2. 配置 (2) 指示 Smooks 对所提供的配置使用 CSV 阅读器。
  3. 配置 (3) 指示 Smooks 为记录片断(参见上述执行报告)创建节点模型。每个记录的节点模型都将覆盖前一个片断产生的节点模型,所以任一已知时刻内存中都绝不会出现一个以上(作为一个节点模型)的 CSV 记录。
  4. 配置 (4) 指示 Smooks 在每个片断结束时将 beanId“csv_record_as_xml”的内容路由到指定的 JMS 目的地。
  5. 配置 (5) 指示 Smooks 在每个片断结束时运用指定的 FreeMarker 模板 (5.a)。该模板操作的结果绑定到 beanId“csv_record_as_xml”(5.b)。

FreeMarker 模板 (5.a) 也可以直接在 Smooks 配置中定义(在<ftl:template></ftl:template> 元素内部),但在这个例子中我们将其定义在外部文件:

<#assign csvRecord = .vars["csv-record"]> <#-- special assignment because csv-record has a hyphen -->

<
customer fname='${
csvRecord.firstname}' lname='${csvRecord.lastname}' >

<gender>${
csvRecord.gender}<gender>

<age>${
csvRecord.age}<age>

<nationality>${
csvRecord.country}<nationality>

<customer>

以上的 FreeMarker 模板引用了“csv-record”片断节点模型。(译注:由于原文编辑错误,导致 HTML 代码中虽有 csv-record 字样,但在展示到浏览器中却没有出现)

Java 绑定

Smooks 可以有效地被用来从任意所支持的源数据格式来生成 Java 对象模型。生成后的对象模型本身可以作为最终结果使用,也可以被用作模板操作的模型,即生成后的对象模型(存储在 bean 上下文里)可供模板技术(就像运用节点模型一样)使用。

再次继续我们的 CSV 实例。我们有一个消费者 Java 类,以及一个性别枚举类型(已省略 getter/setter):

public class Customer {

private String firstName;

private String lastName;

private Gender gender;

private int age;

}
public enum Gender {

Male,

Female

}

从 CSV 流中生成一组这一消费者对象的 Smooks 配置会像是这样:

<xml version="1.0"?>

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"

xmlns:jb
="http://www.milyn.org/xsd/smooks/javabean-1.1.xsd">
(1) <csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />

(2) <jb:bindings beanId="customerList" class="java.util.ArrayList" createOnElement="csv-set">

(2.a)
<jb:wiring beanIdRef="customer" />

<jb:bindings>

(3)

<jb:bindings beanId="customer" class="com.acme.Customer" createOnElement="csv-record">

<jb:value property="firstName" data="csv-record/firstName" />

<jb:value property="lastName" data="csv-record/lastName" />

<jb:value property="gender" data="csv-record/gender" decoder="Enum" >

(3.a)
<jb:decodeParam name="enumType">com.acme.Genderjb:decodeParam>

<jb:value>

<jb:value property="age" data="csv-record/age" decoder="Integer" />

<jb:bindings><smooks-resource-list>
  1. 配置 (1) 指示 Smooks 对所提供的配置使用 CSV 阅读器。
  2. 配置 (2) 指示 Smooks 在遇到消息的开始时(该元素)创建一个 ArrayList 的实例,并将其绑定到 beanId“customerList”的 bean 上下文中。我们想要将 (2.a) 实例的“customer”bean(3) 装配到这一 ArrayList。
  3. 配置 (3) 指示 Smooks 在遇到每一元素的开时时创建一个 Customer 类的实例。每个元素都定义了一个值绑定,从事件流中选择数据并将这一数据解码后的值绑定到当前 Customer 实例的指定属性中。配置 (3.a) 告诉 Smooks 对 Gender 属性使用Enum解码器。

当然,上述分拆、转换和路由用例的一个变体可能是将生成好的 Customer 对象路由到 JMS 队列,而不是一个 FreeMarker 模板所产生的 XML:

<xml version="1.0"?>

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"

xmlns:csv
="http://www.milyn.org/xsd/smooks/csv-1.1.xsd"

xmlns:jms
="http://www.milyn.org/xsd/smooks/jms-routing-1.1.xsd"

xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.1.xsd"><params>

<param name="stream.filter.type">SAXparam>

params><csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />



<jms:router routeOnElement="csv-record" beanId="
customer" destination="xmlRecords.JMS.Queue" /> <jb:bindings beanId="customer" class="com.acme.Customer" createOnElement="csv-record">

<jb:value property="firstName" data="csv-record/firstName" />

<jb:value property="lastName" data="csv-record/lastName" />

<jb:value property="gender" data="csv-record/gender" decoder="Enum" >

<jb:decodeParam name="enumType">com.acme.Genderjb:decodeParam>

jb:value>

<jb:value property="age" data="csv-record/age" decoder="Integer" />

jb:bindings>
<smooks-resource-list>

并且事情再复杂一点也没关系,可以对每个 csv 记录执行多路由操作,可以将 Customer 对象路由到 JMS 队列同时路由到 FreeMarker 产生的 XML 消息以归档。

性能

这一问题不可避免地一次次被提起。我们对 Smooks 进行了许多次的随机基准测试,以下的小节就是我们得到的普遍结果。

  • Smooks 核心过滤开销: Smooks 内核使用 SAX 过滤器(使用 Xerces 作为 XML 阅读器)对 XML 进行处理,在没有配置访问者逻辑的情况下,较使用相同 SAX 解析器直接进行的 SAX 处理增加了大概百分之五到十的开销。
  • Smooks 模板开销: 在早期的 Smooks 版本中,为了对比“通过 Smooks 运用 XSLT”和“单独使用 XSLT”的开销,我们再次执行了一些基准测试以期对其进行确定。Smooks 当时(以及现在)仅通过 DOM 过滤器来支持 XSLT。与基于 DOM 的 XSLT 应用相比, Smooks 增加了百分之五到十五的开销,准确值取决于 XSL 处理器。
  • Smooks Java 绑定开销:我们对于这一点的结果仅仅是基于跟一个主要的“XML 到 Java”开源绑定框架的对比。我们的发现是对于较小的消息(如小于 10k),Smooks 稍有些慢,但对于大一点的消息而言就快得多了。

今天,Smooks 正被应用于好些个任务关键的生产环境。每当我们收到任何关于性能的询问时,其原因总是归咎于某种配置问题(比如使执行报告产生器 一直保持开启状态)。一旦将其解决,用户总会对性能非常满意。这虽然并非一个十分恰当的证据,但是它告诉了我们 Smooks 在性能方面不是“软蛋”。

总的来说,Smooks 内核是相当高效的,较标准的基于 SAX 的 XML 处理而言只增加了相对较低的开销。在这以后,性能取决于所配置的访问者逻辑,它的目的和效率表现。

Smooks 的下一步

Smooks v1.2 的首要目标是提供更多处理 EDI 消息的工具。我们同样希望对某些更流行的 EDI 消息类型提供开箱即用的支持。

如前所述,Smooks 开发的另一重要工作将会是继续 JBoss Tools 项目,构建一个 Smooks 的 Eclipse 编辑器。

总结

希望这一文章能让读者对 Smooks 及其核心功能有个更好的了解。我们希望人们能下载Smooks ,瞧一瞧看一看,并提供些反馈。

查看英文原文: Structured Event Streaming with Smooks


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

评论

发布