精彩的人生

好好工作,好好生活

BlogJava 首页 新随笔 联系 聚合 管理
  147 Posts :: 0 Stories :: 250 Comments :: 0 Trackbacks

#

     摘要: 在讨论Xerdoc DSearch的架构的时候,我们就讨论决定采用Eclipse Plugin Framework,可惜那时Eclipse Plugin Framework和SWT以及其它耦合比较大,因此,决定借鉴Eclipse Plugin Framework的思想,来实现一个自己的轻量级的Plugin Framework。 一晃已经过去快一年了,其实非常早就想把自己研究Eclipse Plu...  阅读全文
posted @ 2005-12-15 11:55 hopeshared 阅读(2077) | 评论 (3)编辑 收藏

 

简单地说,SDO是一种数据应用程序开发框架,它包括一个体系结构和相应的 API。SDO 能够实现以下操作:

  • 简化 J2EE 数据编程模型。
  • 抽象面向服务体系结构(SOA)中的数据。
  • 统一数据应用程序的开发。
  • 支持和集成 XML。
  • 结合 J2EE 模式和最佳实践。

在这篇介绍 SDO 框架的文章中,我们将尝试着解释 SDO 背后的动机,以及 SDO 与其他规范的区别,然后描述 SDO 的组成,最后,通过一个示例 SDO 应用程序说明 SDO 的用法。

为什么要使用 SDO?
对于服务数据对象(SDO),大多数开发人员要问的第一个问题就是为什么要使用 SDO。难道 J2EE 本身还不够庞大、不够复杂(而且难以掌握)吗?Java 环境中不是已经有其他支持 XML 的框架了吗?所幸的是,该问题的答案能够让我们多数人感到满意:SDO 是作为简化 J2EE 数据编程模型的方法出现的,它能够让 J2EE 开发人员把更多的时间用于应用程序的业务逻辑。

服务数据对象框架为数据应用程序开发提供了统一的框架。通过 SDO,您不需要熟悉特定于技术的 API,就能访问和利用数据。您只需要知道一种 API,即 SDO API,它允许您处理来自多种数据源的数据,其中包括关系数据库、实体 EJB 组件、XML 页面、Web 服务、Java Connector Architecture、JavaServer Pages 页面等。

注意,我们使用了 框架这一词。这是比照 Eclipse 框架的说法。由于设计的基础坚固而且可以扩展,所以 Eclipse 能够集成各种工具。与此类似,对于采用 SDO 的应用程序,SDO 也是一种框架,这类应用程序在 SDO 模型上是一致的。

与其他某些数据集成模型不同的是,SDO 没有停留在数据抽象上。SDO 框架还结合了很多 J2EE 模式和最佳实践,从而使应用程序很容易结合经过验证的体系结构和设计。比方说,今天的多数 Web 应用程序百分之百的时间没有(或不能)连接到后端系统,因此 SDO 支持不连接的编程模型。同样的,今天的应用程序往往非常复杂,包含很多层次。如何存储数据、如何发送数据、如何在 GUI 框架中将它们提供给终端用户?SDO 编程模型提供的应用模式能够清晰地划分不同的问题。

XML 在分布式应用程序中日渐普及。比如,XML Schema (XSD) 被用来定义应用程序数据格式中的业务规则。XML 本身也可以用于改进交互性:Web 服务使用基于 XML 的 SOAP 作为消息格式。XML 是推动 SDO 的一个重要原因,SDO 框架支持并集成了 XML。

各种技术的比较
前面已经提到,SDO 并不是提出解决分布式应用程序中数据集成问题的惟一技术。下面将分别讨论 SDO 和类似的编程框架 JDO、JAXB 和 EMF 的优劣。

SDO 和 WDO
Web 数据对象(或 WDO)是随着 IBM WebSphere® Application Server 5.1 和 IBM WebSphere Studio Application Developer 5.1.2 发布的 SDO 早期版本的名称。如果使用过 WebSphere Studio 5.1.2,那么您对 SDO 可能已经有所了解,虽然您可能习惯看到将它标记为 WDO,比如在数据库的名字中。忘掉 WDO 吧,它现在的名字是 SDO!

SDO 和 JDO
JDO 代表 Java Data Object(Java 数据对象)。JDO 已经通过 Java 社区进程(JCP)标准化了 1.0 版,2003 年 5 月推出了维护版 1.0.1,现在已经为 2.0 版成立了 JCP 专家组。JDO 针对 Java 环境中的数据编程提供了一种通用 API,用于访问存储在不同数据源中的数据,如数据库、文件系统或者事务处理系统。JDO 保持了 Java 对象(图)之间的关系,同时允许对数据的并发访问。

JDO 希望简化和统一 Java 数据编程,以便开发人员能够专注于业务逻辑,而不是底层的技术,从这一点上说,其目标和 SDO 是相同的。但主要的区别在于,JDO 仅考虑持久性问题(J2EE 数据层或者企业信息系统(EIS)层),而 SDO 更具一般性,关注的是不同 J2EE 层次间数据流的表示,比如表示层和业务层。

有趣的是,SDO 可以结合 JDO 一起使用,JDO 作为数据源,SDO 来访问它,这就是 Data Transfer Object(数据传输对象,DTO)设计模式的具体运用。同样,SDO 也可以结合实体 EJB 组件和 Java Connector Architecture(Java 连接器体系结构,JCA)使用,目的是提供统一的数据访问。

SDO 和 EMF
EMF 代表 Eclipse Modeling Framework(Eclipse 建模框架)。EMF 根据使用 Java 接口、XML Schema 或者 UML 类图定义的数据模型生成统一的元模型(称为 Ecore),可以将元模型与框架结合在一起使用,创建高质量的模型实现。EMF 提供了持久性、一个有效的反射类属对象操纵 API 和一个变更通知框架。EMF 还包括用来构建 EMF 模型编辑器的一般的重用类。

EMF 和 SDO 都可以处理数据表示。事实上,IBM 的 SDO 参考实现就是一种 SDO 的 EMF 实现,后面我们还要用到该实现。还可以根据 UML 模型定义或者 SDO 本身,用 EMF 代码生成来创建 SDO 实现的某些部分。SDO 实现基本上是 EMF 中的一小层(方面),它是作为 EMF 项目的一部分打包和提供的。关于 EMF 的更多信息,请参阅 参考资料

SDO 和 JAXB
JAXB 代表 Java API for XML Data Binding(XML 数据绑定 Java API)。JAXB 1.0 在 2003 年 1 月由 JCP 发布。JCP 专家组已经制定了 2.0 版的初步草案。JAXB 是关于 XML 数据绑定的,也就是说,将 XML 数据表示成内存中的 Java 对象。作为 Java 语言的 XML 绑定框架,JAXB 可以节约解析和创建 XML 文档的时间。(事实上,通过它,您完全不需要和 XML 打交道。)JAXB 为您执行编组/序列化(Java 到 XML)和解组/并行化(XML 到 Java)。

SDO 定义了自己的 Java 绑定框架,但是走得还要远一些。JAXB 仅关注 Java 到 XML 的绑定,而绑定到 SDO 的不仅仅是 XML 数据。如前所述,SDO 提供了对不同类型数据的统一访问,XML 仅是其中的一种数据类型。SDO 还提供了静态和动态 API *,而 JAXB 仅提供了静态绑定。

* 注意,本文中示例应用程序仅使用了动态 SDO,虽然 EMF 代码生成器也为数据对象的静态代码生成提供了完整的支持。

SDO 和 ADO .NET
ADO 代表 ActiveX Data Objects(ActiveX 数据对象),但是在 .NET 上下文中,其含义已经发生了变化。ADO .NET 提供了 .NET 框架下不同层次间的统一数据访问机制。

ADO .NET 和 SDO 具有相同的动机,都是用来支持 XML 和分布在多个层上的应用程序。除了技术性的差异外,这两项技术主要的不同在于:ADO .NET 用于 Microsoft .NET 平台,是一种私有的技术;而 SDO 用于 Java (J2EE) 平台,通过 Java 社区进程实现标准化。

SDO 的组成部分
这一节将介绍 SDO 的体系结构。我们将介绍组成框架的不同组成部分和它们之间的协作。首先要讨论的三个组成部分之一是 SDO 的“概念性”特征,API 中没有对应的接口。

SDO 客户机
SDO 客户机使用 SDO 框架处理数据。SDO 客户机使用的不是特定于技术的 API 和框架,而是 SDO 编程模型和 API。SDO 客户机处理 SDO 数据图(参见 图 1),不需要了解所处理的数据是如何持久保存或者序列化的。

Data 中介服务
数据中介服务(DMS)负责从数据源创建数据图、依据数据图的变化更新数据源。数据中介框架不属于 SDO 1.0 规范,换句话说,SDO 1.0 没有涉及具体的 DMS。常见的 DMS 有 JDBC DMS、实体 EJB DMS 和 XML DMS 等。

数据源
数据源不限于后端数据源(如持久存储数据库)。数据源以自己的格式保存数据。只有 DMS 访问数据源,SDO 应用程序不访问数据源。SDO 应用程序可能只使用数据图中的数据对象。

下面介绍的每个组件对应于 SDO 编程模型的一个 Java 接口。SDO 参考实现(请参阅 参考资料)提供了这些接口基于 EMF 的实现。

数据对象
数据对象是 SDO 的基本组件。事实上,它们是规范名称中的 服务数据对象。数据对象是结构化数据的 SDO 表示。数据对象是通用的,它们提供了 DMS 创建的结构化数据的公共视图。比方说,虽然 JDBC DMS 需要知道持久性技术(比如关系数据库),以及如何配置和访问数据,SDO 客户机不需要了解这些细节。数据对象在属性中保存它们的“数据”(稍后还要讨论属性)。数据对象提供了易于使用的创建和删除方法(带有不同签名的 createDataObject()delete() ),获得自身类型(实例类、名称、属性和名称空间)的反射方法。数据对象都链接在一起,包含在数据图中。

数据图
数据图提供了数据对象树的容器。数据图由 DMS 生成,供 SDO 客户机使用。修改后,数据图被回传给 DMS 更新数据源。SDO 客户机可以遍历数据图,读取和修改数据图中的数据对象。SDO 是一种 无连接的体系机构,因为 SDO 客户机与 DMS 和数据源没有连接,所以 SDO 客户机看到的只有数据图。此外,数据图可以包含表示不同数据源中数据的对象。数据图包含一个根数据对象、与根关联的所有数据对象和变更摘要(change summary,参见本文后面的叙述)。当在应用程序组件(比如服务调用期间的 Web 服务请求者和提供者)之间进行传输、组件到 DMS 的传输(或者保存到磁盘)的时候,数据图被序列化为 XML。SDO 规范提供了序列化的 XML Schema。图 1 显示了一个 SDO 数据图。

图 1. SDO 数据图
SDO 数据图

变更摘要
变更摘要包含在数据图中,表示对 DMS 返回的数据图的修改。变更摘要最初是空的(数据图刚返回客户机的时候),随着数据图的变化逐渐填充。在后台更新时,DMS 使用变更摘要将修改应用于数据源。变更摘要提供了数据图中被修改的属性(包括原来的值)、新增和删除的数据对象的列表,从而使 DMS 以递增方式高效地更新数据源。只有当变更摘要日志功能被激活时,才会将信息添加到数据图的变更摘要中。变更摘要提供了让 DMS 打开和关闭日志功能的方法,后面的例子中还将详细对其进行介绍。

属性、类型和序列
数据对象用一系列属性保存其内容。每个属性都有一个类型,该类型既可以是基本类型(如 int )这样的属性类型,也可以是通用数据类型(如 Date ),如果引用的话,还可以是其他数据对象类型。每个数据对象都为属性提供了访问和设置方法(getter 和 setter)。这些访问器方法有不同的重载版本,可以通过传递属性名( String )、编号( int )或者属性元对象本身来访问属性。String 访问器还允许使用类 XPath 的语法访问属性。比如,可以对公司数据对象调用 get("department[number=123]") 来访问编号为 123的第一个部门。序列更加高级,可以保持不同种类的属性-值对列表的顺序。

SDO 和示例安装
我们已经讲了很多的概念和理论,现在要进行一些实际操作了。好消息是您马上就能使用 SDO,而且是免费的。这一节将介绍一个 SDO 示例应用程序,它将在 SDO 的 IBM 参考实现上运行,这个参考实现是 Eclipse Modeling Framework (EMF) 的一部分。我们首先将说明如何安装 EMF 2.0.1(包括 SDO),然后将介绍如何设置本文提供的示例应用程序。

安装 EMF 2.0.1
如果已经安装了 EMF 2.0.1,或者知道怎么安装,那么请跳过这一节,直接阅读下一节。

IBM SDO 1.0 实现和 EMF 2.0.1 一起打包。要使用 SDO,则需要安装 EMF 2.0.1 *。您可以按照 EMF 站点上的说明使用 Eclpise 更新管理器,也可以按照下面的步骤操作。

*EMF 2.0.0 中也包含 SDO 1.0 的实现。

EMF 主页中的“快速导航”部分可以找到下载链接页面,选择下载“v2.x: EMF 和 SDO”。安装 EMF 之前一定要阅读安装要求。安装 EMF 2.0.1 之前至少要安装了 Eclipse 3.0.1 和 Java Development Kit (JDK) 1.4。一定要选择 EMF 2.0.1 发行版本。打包类型建议选择“All”:emf-sdo-xsd-SDK-2.0.1.zip,这样,就可以在一个文件中找到源代码、运行文件和文档。如果愿意,还可以下载 SDO 的最小安装包“EMF & SDO RT”:emf-sdo-runtime-2.0.1.zip。

在解压 Eclipse 的目录中解压 zip 文件(压缩包中的文件组织结构为 eclipse/plugins/...)。启动 Eclipse,然后选择 Help>About the Eclipse Platform,检查 EMF 是否安装成功。单击 Plug-in Details按钮,确保 org.eclipse.emf.* 插件在 2.0.1 层次上。有 6 个与 SDO 有关的插件:

  • org.eclipse.emf.commonj.sdo
  • org.eclipse.emf.ecore.sdo
  • org.eclipse.emf.ecore.sdo.doc
  • org.eclipse.emf.ecore.sdo.edit
  • org.eclipse.emf.ecore.sdo.editor
  • org.eclipse.emf.ecore.sdo.source

运行时只需要两个插件: org.eclipse.emf.commonj.sdoorg.eclipse.emf.ecore.sdo ,如果选择仅安装运行时插件,那么您只能看到这两个插件。这样就完成了 EMF 的安装。

安装示例 SDO 应用程序
下一步是在工作区中添加本文所用的 SDO 示例应用程序,步骤如下:

  1. 启动 Eclipse 并创建一个新的 Plug-In Project。
  2. 将项目命名为 SDOSample,选择源文件夹 src和输出文件夹 bin,建立 Java 源代码。
  3. 单击 Next
  4. 取消选择“Generate the Java class that controls the plug-in's life cycle(生成控制插件生命期的 Java 类)”选项并单击 Finish

然后,单击本文顶端或底端的 Code图标(或者参阅 下载部分)下载 j-sdoSample.zip,并将其解压缩到 SDOSample 目录中(在 Eclipse 窗口中选择 Import... >Zip file)。一定要保留文件夹结构并覆盖原来的文件,这样,就可以用 j-sdoSample.zip 中的文件代替 SDOSample 项目。

注意:SDOSample 是作为 Eclipse 插件项目打包的,因此不需要自己设置库依赖关系。但是,在本例中,只包含 Java 代码,如果在 CLASSPATH 中包含 EMF 和 SDO 库(JAR 文件),也可将 SDOSample 作为独立的应用程序运行。

环境设置应该与下面截屏图中的类似。

图 2. Eclipse 环境
Eclipse 环境

现在开始使用这个示例 SDO 应用程序。

一个简单的 SDO 应用程序
本文后面将使用的示例应用程序从功能上说很有限,但它可以帮助您更好地理解 SDO。该应用程序包括两部分,分别放在两个包中:dms 和 client。

SDO 1.0 没有规定标准 DMS API。因此我们为这个例子设计了自己的 DMS 接口,它包含两个方法,如清单 1 所示。

清单 1. DMS 接口

/**
 * A simple Data Mediator Service (DMS) that builds
 * SDO Data Graphs of Employees and updates
 * a backend data source according to a Data Graph. 
 */
public interface EmployeeDMS
{
  /**
   * @param employeeName the name of the employee.
   * @return an SDO Data Graph with Data Objects for
   * that employee's manager, that employee,
   * and that employee's "employees".
   */
  DataGraph get(String employeeName);

  /**
   * updates backend data source according to dataGraph.
   * @param dataGraph Data Graph used to update data source.
   */
  void update(DataGraph dataGraph);
}

客户机将实例化 DMS,并调用 get() 方法访问特定的雇员:Big Boss、Wayne Blanchard 和 Terence Shorter。它以用户友好的方式在控制台中打印这些雇员的信息,然后更新 Terence Shorter 及其雇员的部门信息。最后调用 DMS 的 update() 方法,传递更新的 Terence Shorter 数据图。

注意,为了便于示范,我们没有实现数据源组件,但在 DMS 中,有一些如何根据查询构建数据图的“硬编码”知识。图 3 显示了 DMS 正在使用的雇员层次结构。

图 3. Big Boss 公司的雇员
Big Boss 公司的雇员

图中可以看出,DMS 背后的虚拟公司有 4 个雇员,层次结构如下:

  • Big Boss 没有上司,Terence Shorter 是其下属。
  • Terence Shorter 以 Big Boss 作为自己的上司,John Datrane 和 Miles Colvis 是其下属。
  • John Datrane 的上司是 Terence Shorter,没有下属。
  • Miles Colvis 的上司是 Terence Shorter,没有下属。

运行示例应用程序
右击 SDOClient.java,然后选择 Run>Java application运行上述示例应用程序,结果如下:

清单 2. 应用程序的控制台输出

********* EMPLOYEE INFORMATION *********

Name: John Datrane
Number: 4
Title: Mr.
Department: Procurement
Is manager?: no

DIRECT MANAGER:

Name: Terence Shorter
Number: 2
Title: Mr.
Department: Financing
Is manager?: yes

****************************************

NO INFORMATION AVAILABLE ON EMPLOYEE Wayne Blanchard

********* EMPLOYEE INFORMATION *********

Name: Terence Shorter
Number: 2
Title: Mr.
Department: Financing
Is manager?: yes

DIRECT MANAGER:

Name: The Big Boss
Number: 1
Title: Mr.
Department: Board
Is manager?: yes

DIRECT EMPLOYEES:

Name: Miles Colvis
Number: 3
Title: Mr.
Department: Accounting
Is manager?: no

Name: John Datrane
Number: 4
Title: Mr.
Department: Procurement
Is manager?: no

[Total: 2]
****************************************

DMS updating Terence Shorter
 (changed department from "Financing" to "The new department")
DMS updating Miles Colvis
 (changed department from "Accounting" to "The new department")
DMS updating John Datrane
 (changed department from "Procurement" to "The new department")

现在我们来分析应用程序的各个组成部分是如何工作的。

客户机
SDO 客户机例示了 DMS,并从中获得不同雇员的数据图。得到数据图后,通过根对象(使用 SDO 动态 API)遍历和访问数据对象,如下所示:


// Get the SDO DataGraph from the DMS.
DataGraph employeeGraph = mediator.get(employeeName);
...
// Get the root object
DataObject root = employeeGraph.getRootObject();
...
// get the employee under the manager
employee = theManager.getDataObject("employees.0");

客户机调用动态 SDO 访问器 API,从数据对象中获得信息,并将其显示到控制台上:


System.out.println("Name: " + employee.getString("name"));
System.out.println ("Number: " + employee.getInt("number"));
...
System.out.println ("Is manager?: " + 
  (employee.getBoolean("manager") ? "yes" : "no") + "\n");

我们已经看到客户机是如何获取信息(读)的,但是如何写入信息呢?更准确地说,客户机如何修改对象?SDO 客户机一般使用 DataObject 写访问器方法更新数据对象。比如,下面的代码修改从雇员 Terence Shorter 获得的数据图:


employee.setString("department", newDepartmentName);

注意,客户机没有调用日志方法。DMS 负责对数据图的变更摘要调用 beginLogging()endLogging() 记录日志。

数据图
可以将数据图的数据格式(模型)看作 DMS 与客户机之间的抽象。客户机期望从 DMS 得到数据图,DMS 也知道如何创建数据读(包括从后端数据源中读取和更新数据)。如果熟悉 XML 或者 Web 服务,那么可以将数据图模型看作定义数据对象 XML Schema(XSD)。数据图本身就类似 XML 实例文档。事实上,XML Schema 也是定义 SDO 模型的一种方法。

注意,数据图及其模型总是能够序列化为 XML。在 SDOClient.java 中将 debug 变量设为 true ,就可以看到运行时在控制台中显示的数据图的序列化版本,与清单 3 类似。

清单 3. 数据图的序列化版本

<?xml version="1.0" encoding="UTF-8"?>
<sdo:datagraph xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:company="http://com.example.company.ecore"
  xmlns:sdo="commonj.sdo"
  xmlns:sdo_1="http://www.eclipse.org/emf/2003/SDO">
  <changeSummary>
    <objectChanges key="#//@eRootObject">
      <value xsi:type="sdo_1:EChangeSummarySetting"
        featureName="department" dataValue="Board"/>
    </objectChanges>
  </changeSummary>
  <company:Employee name="The Big Boss"
    number="1" department="The new department" title="Mr."
    manager="true">
    <employees name="Terence Shorter" number="2"
      department="The new department" title="Mr." manager="true">
      <employees name="Miles Colvis" number="3"
        department="The new department" title="Mr."/>
      <employees name="John Datrane" number="4"
        department="The new department" title="Mr."/>
    </employees>
  </company:Employee>
</sdo:datagraph>

在本例中,数据图由 Employee 数据对象(以及变更摘要)组成。 Employee 包含姓名、编号、部门、职务、上司(管理该雇员的另一位雇员)和下属(该雇员管理的其他雇员)等属性。该例中,雇员已经位于硬编码的数据源中时,DMS 返回的数据图的格式总是雇员的上司(如果有的话)、雇员及其直接下属(如果有的话)。

DMS 构建数据图
SDO 1.0 没有规定 DMS API,API 应该包含数据图模型本身的设计和创建。因为建立对数据源的访问要考虑很多种情况,设计数据图本身可以作为另一篇文章的主题。

对这个例子,我们将采用 DMS 通过动态 EMF API 定义的雇员模型。示例数据图没有 XSD 这样的模型文档。实际上,因为对象是动态生成的,所以这意味着没有生成 Employee Java 类。如果使用静态方法,就会生成这样的类。

DMS 使用不同的数据访问 API(JDBC、SQL 等)从不同数据源获取的信息。但是,一旦从后端(该例中是硬编码的知识)检索到信息,DMS 就转而使用 EMF API(eSet、eGet)而非 SDO API 来创建数据对象的数据图。这种方法能得到较好的性能,但缺点是不能跨 SDO 实现移植。

对于主要关注性能,同样的 DMS 设计也可使用 SDO API 实现。在这种情况下,DMS 类( employeeClassemployeeNameFeature 等)中缓冲的元对象,将代替 EMF 类型 EClassEAttributeEReference 等),成为 commonj.sdo.Typecommonj.sdo.Property 的类型。此外,如果性能不重要,那么可以方便地使用基于 String 的 SDO API(如 setBoolean(String path, boolean value) ),因此不需要缓冲元对象。不幸的是,这种解决方案虽然更方便,但运行起来也更慢。

下面的代码片段说明了 SimpleEmployeeDataMediatorImpl.java 中 Employee 模型的定义。但这些代码并没有构建 SDO 对象,构建的仅仅是 SDO 对象的模型:


protected EClass employeeClass;
protected EAttribute employeeNameFeature;
protected EReference employeeEmployeesFeature;
...

employeeClass = ecoreFactory.createEClass();
employeeClass.setName("Employee");

EAttribute employeeNameFeature = ecoreFactory.createEAttribute();
...
    
// employees (that the employee manages)
employeeEmployeesFeature = ecoreFactory.createEReference();
employeeEmployeesFeature.setContainment(true);
...

EPackage employeePackage = ecoreFactory.createEPackage();
employeePackage.getEClassifiers().add(employeeClass);
...

注意,我们使用值 trueemployees EReference 调用 setContainment ,因此每个雇员都“包含”他或她的下属。否则,嵌套的雇员就不会(即被包含)在数据图中,变更摘要也只能包含根对象的修改,而不能包含其他雇员的修改。

建模 SDO
您可能想“很有趣,但是这样只能得到 EMF 对象而不是 SDO 对象,还有什么窍门吗?”当然有,而且很简单。 Employee EClass 属于 employeePackage EPackage ,并且包含下面的调用:


// Have the factory for this package build SDO Objects
employeePackage.setEFactoryInstance(
  new DynamicEDataObjectImpl.FactoryImpl());

运行时,这个工厂将创建 DynamicEDataObjectImpl 类型的对象,该对象实现了 DataObject 接口(即它是一个 SDO 数据对象),而不是默认的 DynamicEObjectImpl 接口,并且可以智能地创建普通的 EMF 对象。SDO 和 EMF 对象之间的关系就很清楚了:SDO 对象就是同时实现了 SDO DataObject 接口的 EMF 对象。事实上,这些附加方法的实现是委派给核心 EMF 方法实现的。

创建 SDO 实例
现在,已经拥有了我们的数据对象,就可以建立 Employee 的实例并设置不同的属性了。如前所述,我们将使用 EMF API 来最大程度地提高性能。


EObject eObject = EcoreUtil.create(employeeClass);

// Note: we could cast the object to DataObject,
// but chose to use EObject APIs instead.
eObject.eSet(employeeNameFeature, name);
eObject.eSet(employeeNumberFeature, new Integer(number));
... ...

可以使用“employees”引用把雇员“链接”在一起,比如:


((List)bigBoss.eGet(employeeEmployeesFeature)).add(terence);

一旦创建了数据对象,还需要将其连接到数据图中。我们首先要调用数据图的 setRootObject() 方法,传递希望用作根的数据对象,这里使用 Employee The Boss


EDataGraph employeeGraph = SDOFactory.eINSTANCE.createEDataGraph();
... ...
employeeGraph.setERootObject(rootObject);

继续讨论数据图之前,要先开始记录日志。如果希望使用 SDO,那么在改动数据图之前,要先调用其变更摘要的 beginLogging() 方法。基本的原理就是清除以前的变化,开始监听新的修改。


// Call beginLogging() so that the Change Summary is
// populated when changes are applied to the Data Graph.
// The DMS should call beginLogging() and endLogging(),
// not the client.
employeeGraph.getChangeSummary().beginLogging();

DMS 的另一项任务(按照 EmployeeDataMediator 接口的定义)是根据 SDO 客户机提供的数据图更新后端数据源。

DMS:更新数据源
DMS 应该使用 SDO 的强大特性更新后端数据源,具体地说,就是要使用其变更摘要。使用数据图的变更摘要有许多不同的方法。该例中,我们将考察变更摘要树中引用的所有数据对象,并从中获得新的数据对象。

清单 4. DMS 根据数据图更新后端数据源

/**
 * Update the DMS's backend data to reflect changes
 * in the data graph.
 * Since this DMS has no actual backend data and therefore
 * has nothing to update, we will just navigate
 * the change summary and report (print) what's changed.
 */
public void update(DataGraph dataGraph)
{
  ChangeSummary changeSummary = dataGraph.getChangeSummary();
 
  // Call endLogging to summarize changes.
  // The DMS should call beginLogging() and endLogging(),
  // not the client.
  changeSummary.endLogging();
 
  // Use SDO ChangeSummary's getChangedDataObjects() method.
  List changes = changeSummary.getChangedDataObjects();
  for (Iterator iter = changes.iterator(); iter.hasNext();)
  {
    DataObject changedObject = (DataObject)iter.next();
    System.out.print("DMS updating " +
      changedObject.getString("name"));

    for (Iterator settingIter = changeSummary.getOldValues(
      changedObject).iterator(); settingIter.hasNext();)
    {
      ChangeSummary.Setting changeSetting =
        (ChangeSummary.Setting)settingIter.next();
      Property changedProperty = changeSetting.getProperty();
      Object oldValue = changeSetting.getValue();
      Object newValue = changedObject.get(changedProperty);

      System.out.print(" (changed: " + changedProperty.getName() +
        " from \"" + oldValue + "\" to \"" + newValue + "\")");
      // If not a simple example, we could update the backend here.
    }

    System.out.println();
  }
}

该例中根本没有后端更新。但在实际应用时,后端更新应该在这个方法中完成。

DMS 首先要对数据图的更新摘要调用 endLogging() ,从客户机取回数据图,以便进行后端更新。这样将关闭变更记录,从而提供自 beginLogging() 调用以来(通常在创建之后调用)对数据图所做修改的摘要。这种格式支持 DMS 高效、递增地更新后端数据源。变更摘要中的修改分为三种类型:

  • 对象变更包含数据图中属性已经被修改的那些数据对象的引用、被修改的属性和修改之前的属性值。DMS 可以使用原来的属性值确保后端数据在此期间没有被别的人修改。

  • 对象创建包含添加到数据图中的数据对象。将这些对象表示了需要添加到后端数据结构中的新数据。

  • 对象删除包含从数据图中删除的数据对象。这些对象表示了需要从后端数据结构中删除的数据。

注意,我们使用标准的 SDO API 检查数据图的变化,虽然也可以使用 EMF ChangeDescription API(而不是 SDO 的 ChangeSummary)。该例中仅仅更新简单的属性值,对性能的影响不是很明显。但在其他情况下,比方说要更新大量的属性,使用 EMF API 可以显著地提高性能。比如,假设要从数百个雇员的列表中删除一个雇员。在这种情况下,ChangeSummary 只能提供对原有数百个雇员列表中的原有值的访问。 而 EMF 的 ChangeDescription 接口还可以提供更具体的信息,如“删除某个索引位置的雇员”,这就有用得多。

还要注意的是,该变更摘要中只修改了一个对象,没有删除或者添加对象。如果使用 SDO 实现从数据图中删除对象,还要注意类型为 objectsToAttach 的元素。该名称实际上是对象删除的 EMF ChangeDescription 名。这些元素是那些已经删除但是在回滚时需要放回图中的数据对象,这就是变更的 EMF 视图。总而言之, objectsToAttach == deleted objects(删除的对象)

调试应用程序
如果在示例应用程序中将 debug 变量设为 true ,那么就可以执行以下调用,查看数据图的序列化版本。


((EDataGraph) dataGraph).getDataGraphResource().save(System.out, null);

还可以使用 Eclipse 调试环境。比如,我们建议在 SDOClient.java 第 100 行设置一个断点,并调试 SDOClient (像在 Java 应用程序中那样)。然后,在调试窗口中就可以看到内存中的数据图(在 Variables 下)和它的数据对象(Boss、Terrence Shorter 等),如图 4 所示。

图 4. 在调试模式下观察数据对象
调试

还可以用这种方法查看变更摘要,如图 5 所示。

图 5. 在调试模式下观察变更摘要
调试

上面的截屏图看起来很复杂,现在看来用处也不大,但是当您调试自己的 SDO 应用程序并查看数据对象和变更摘要的内容时,就会用到它。

结束语
本文简要描述了 SDO 及其功能。我们通过一个示例应用程序说明了 SDO 的一些功能。更多参考信息,请参阅 Eclipse 帮助系统下的 SDO API 文档。该规范仍在发展和改进之中。比如,SDO 1.0 强调了 SDO 的客户机方面,而没有指定一个 DMS API。SDO 目前正通过 JCP 实现标准化,请关注有关的进展情况。因为 SDO 非常灵活,设计 SDO 应用程序时需要做很多决策。这些决策将影响程序的重用性和性能。因此,在编码之前,应该认真考虑应用模式和应用程序数据的特征。

参考资料

下载
Name Size Download method
j-sdoSample.zip FTP
*关于下载方法的信息
作者简介
Bertrand Portier 是 IBM 的软件开发人员,他是在 Eclipse.org 上开发 SDO 参考实现的 EMF 开发小组的主要成员。他拥有使用 J2EE 的丰富经验,曾经参与过 IBM 产品和 Web 服务领域的开发,并帮助 IBM 客户开发自己的分布式应用程序。


Frank Budinsky 是 Eclipse.org 的 Eclipse Modeling Framework 项目经理,参与了 EMF 框架的架构和实现,其中包括 SDO 的参考实现。他是 IBM Software Group 的工程师,有多年框架和生成器设计方面的经验。Frank 是权威的 EMF 参考书 Eclipse Modeling Framework, A Developer's Guide 的主要作者。



原文地址:http://www-128.ibm.com/developerworks/cn/java/j-sdo/
posted @ 2005-12-14 16:51 hopeshared 阅读(1403) | 评论 (2)编辑 收藏

原文:http://spaces.msn.com/members/ping-yuan/PersonalSpace.aspx?_c=
9.22 SDO学习笔记
今天粗粗看了一眼SDO,略有理解。
 
SDO全称Service Data Objects,是IBM提出的一个框架规范。SDO框架由三部分组成:DMS(Data Mediator Services),Data Graph和DataObject。DMS负责生成Data Graph,Data Graph包含一系列关联的Data Object。客户和DataGraph打交道,而DMS如何生成Data Graph又如何从Data Graph更新后面的数据则无需关心。Data Graph是Disconnected Mode的数据处理方式,即对其进行的修改操作,将不会立刻体现,需要将修改过的Data Graph由DMS来更新到数据源。Data Graph是通过log change summary来实现的。
 
在Spec中说道,Data Graph被序列化为XML也能从XML中被反序列化。这使得在Services和其Caller之间传递DataGraph非常直接。同时也提出了系统内部、系统之间数据交互的一致方式——通过XML序列化过的Data Graph。
 
Data Object可以被动态实现(程序里将看不见具体的Data Object类,比如Employee类,因此也无需定义XML Schema),也可以被静态生成(例如预先建模后使用工具生成,目前IBM基于EMF的RI可以使用EMF来生成)。
 
DMS可以有许多实现方式,在IBm的SDO Specification中并没有任何关于DMS实现方式的规范。实际上,DMS在SDO Spec V2.0里面已经改称DAS(Data Access Services),我们发现这命名和DAO(Data Access Object)模式何其相似。不过这里是Service。那么可以想象在SOA中,我们可以提供这样的DAS来提供数据并作数据更新。难道与DAO类似这将会成为一种SOA模式?
 
更重要的是,DAS可以在J2EE的各层都能被使用。你可以使用JDBC实现DAS用它做一个持久化服务层,你可以用EJB实现DAS并且暴露成Web Service……你甚至可以使用Hibernate、JDO这样的持久化工具来实现DAS。
 
所以我们不可能混淆SDO框架和Hibernate、JDO等工具——因为后者只是持久化工具存在于EIS之上;也无需怀疑SDO的价值——SDO确实可以为整个J2EE应用尤其是SOA提供一个一致的数据处理方式。
 
明天继续研究。希望能深化、纠正现在的理解。
posted @ 2005-12-14 16:46 hopeshared 阅读(1287) | 评论 (1)编辑 收藏

        好像利用Eclipse自定义扩展点的人不多。

        在我以前做的项目中,用了自定义扩展点,但是使用这个扩展点的文件也在这个项目中,整个项目就是一个插件。所以,加载扩展点的方式很简单:

 1IExtensionRegistry registry = Platform.getExtensionRegistry();
 2IExtensionPoint point = registry.getExtensionPoint(“org.eclipse.ui.popupMenus”);
 3IExtension[] extensions = point.getExtensions();
 4for (int i = 0; i < extensions.length; i++){
 5    IConfigurationElement[] elements = extensions.getConfigurationElements();
 6          for (int j = 0; j < elements.length; j++){
 7                String eleType = elements[j].getName();
 8                Class importantClass = Class.forName(element[j].getAttribute(MyConstants.ATTR_MODELCLASS)).newInstance();
 9                ……
10          }

11}

        在一个插件项目中,这样的代码一点问题都没有。

        但是,我现在的项目分为几个插件,扩展点的定义放在了插件项目A中,相关的接口和扩展点实现收集器(这是我自己取的名字)也在A中,但是扩展点的实现是其他的插件,比如插件B就实现了A中定义的扩展点。那么他们之间的依赖关系是

A  <----------  B

        换句话说,在A中看不到B中的代码,那么问题就来了:上面那段代码的第8行将会报出class not found的异常。

        如何解决这个问题?

        可能是我对Eclipse的核心代码太不熟悉了,所以这个问题困扰了我很久。

        解决的方法是,将第8行替换为

WorkbenchPlugin.createExtension(element, MyConstants.ATTR_MODELCLASS);

       这个方法将激活本次实现所在的插件项目的Bundle,问题就解决了。

     
posted @ 2005-12-14 14:04 hopeshared 阅读(2262) | 评论 (8)编辑 收藏

转自 http://www.jdon.com/idea/jsf-struts.htm

  Struts和JSF/Tapestry都属于表现层框架,这两种分属不同性质的框架,后者是一种事件驱动型的组件模型,而Struts只是单纯的MVC模式框架,老外总是急吼吼说事件驱动型就比MVC模式框架好,何以见得,我们下面进行详细分析比较一下到底是怎么回事?

  首先事件是指从客户端页面(浏览器)由用户操作触发的事件,Struts使用Action来接受浏览器表单提交的事件,这里使用了Command模式,每个继承Action的子类都必须实现一个方法execute。

  在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。

  struts重要的表单对象ActionForm是一种对象,它代表了一种应用,这个对象中至少包含几个字段,这些字段是Jsp页面表单中的input字段,因为一个表单对应一个事件,所以,当我们需要将事件粒度细化到表单中这些字段时,也就是说,一个字段对应一个事件时,单纯使用Struts就不太可能,当然通过结合JavaScript也是可以转弯实现的。

  而这种情况使用JSF就可以方便实现,

<h:inputText id="userId" value="#{login.userId}">
  <f:valueChangeListener type="logindemo.UserLoginChanged" />
</h:inputText>

  #{login.userId}表示从名为login的JavaBean的getUserId获得的结果,这个功能使用struts也可以实现,name="login" property="userId"

  关键是第二行,这里表示如果userId的值改变并且确定提交后,将触发调用类UserLoginChanged的processValueChanged(...)方法。

  JSF可以为组件提供两种事件:Value Changed和 Action. 前者我们已经在上节见识过用处,后者就相当于struts中表单提交Action机制,它的JSF写法如下:

<h:commandButton id="login" commandName="login">
  <f:actionListener type=”logindemo.LoginActionListener” />
</h:commandButton>

  从代码可以看出,这两种事件是通过Listerner这样观察者模式贴在具体组件字段上的,而Struts此类事件是原始的一种表单提交Submit触发机制。如果说前者比较语言化(编程语言习惯做法类似Swing编程);后者是属于WEB化,因为它是来自Html表单,如果你起步是从Perl/PHP开始,反而容易接受Struts这种风格。

基本配置

  Struts和JSF都是一种框架,JSF必须需要两种包JSF核心包、JSTL包(标签库),此外,JSF还将使用到Apache项目的一些commons包,这些Apache包只要部署在你的服务器中既可。

  JSF包下载地址:http://java.sun.com/j2ee/javaserverfaces/download.html选择其中Reference Implementation。

  JSTL包下载在http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi

  所以,从JSF的驱动包组成看,其开源基因也占据很大的比重,JSF是一个SUN伙伴们工业标准和开源之间的一个混血儿。

  上述两个地址下载的jar合并在一起就是JSF所需要的全部驱动包了。与Struts的驱动包一样,这些驱动包必须位于Web项目的WEB-INF/lib,和Struts一样的是也必须在web.xml中有如下配置:

<web-app>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>
</web-app>

  这里和Struts的web.xml配置何其相似,简直一模一样。

  正如Struts的struts-config.xml一样,JSF也有类似的faces-config.xml配置文件:


<faces-config>
  <navigation-rule>
    <from-view-id>/index.jsp</from-view-id>
    <navigation-case>
      <from-outcome>login</from-outcome>
      <to-view-id>/welcome.jsp</to-view-id>
    </navigation-case>
  </navigation-rule>

  <managed-bean>
    <managed-bean-name>user</managed-bean-name>
    <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>
</faces-config>

 

  在Struts-config.xml中有ActionForm Action以及Jsp之间的流程关系,在faces-config.xml中,也有这样的流程,我们具体解释一下Navigation:

  在index.jsp中有一个事件:

<h:commandButton label="Login" action="login" />

  action的值必须匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一个login事件,那么事件触发后下一个页面将是welcome.jsp

  JSF有一个独立的事件发生和页面导航的流程安排,这个思路比struts要非常清晰。

  managed-bean类似Struts的ActionForm,正如可以在struts-config.xml中定义ActionForm的scope一样,这里也定义了managed-bean的scope为session。

  但是如果你只以为JSF的managed-bean就这点功能就错了,JSF融入了新的Ioc模式/依赖性注射等技术。

Ioc模式

  对于Userbean这样一个managed-bean,其代码如下:

public class UserBean {
  private String name;
  private String password;

  // PROPERTY: name
  public String getName() { return name; }
  public void setName(String newValue) { name = newValue; }

  // PROPERTY: password
  public String getPassword() { return password; }
  public void setPassword(String newValue) { password = newValue; }
}

<managed-bean>
  <managed-bean-name>user</managed-bean-name>
  <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>

  <managed-property>
    <property-name>name</property-name>
    <value>me</value>
  </managed-property>

  <managed-property>
    <property-name>password</property-name>
    <value>secret</value>
  </managed-property>
</managed-bean>

  faces-config.xml这段配置其实是将"me"赋值给name,将secret赋值给password,这是采取Ioc模式中的Setter注射方式

Backing Beans

  对于一个web form,我们可以使用一个bean包含其涉及的所有组件,这个bean就称为Backing Bean, Backing Bean的优点是:一个单个类可以封装相关一系列功能的数据和逻辑。

  说白了,就是一个Javabean里包含其他Javabean,互相调用,属于Facade模式或Adapter模式。


  对于一个Backing Beans来说,其中包含了几个managed-bean,managed-bean一定是有scope的,那么这其中的几个managed-beans如何配置它们的scope呢?

<managed-bean>
  ...
  <managed-property>
    <property-name>visit</property-name>
    <value>#{sessionScope.visit}</value>
  </managed-property>

  这里配置了一个Backing Beans中有一个setVisit方法,将这个visit赋值为session中的visit,这样以后在程序中我们只管访问visit对象,从中获取我们希望的数据(如用户登陆注册信息),而visit是保存在session还是application或request只需要配置既可。

UI界面

  JSF和Struts一样,除了JavaBeans类之外,还有页面表现元素,都是是使用标签完成的,Struts也提供了struts-faces.tld标签库向JSF过渡。

  使用Struts标签库编程复杂页面时,一个最大问题是会大量使用logic标签,这个logic如同if语句,一旦写起来,搞的JSP页面象俄罗斯方块一样,但是使用JSF标签就简洁优美:

<jia:navigatorItem name="inbox" label="InBox"
  icon="/images/inbox.gif"
  action="inbox"
  disabled="#{!authenticationBean.inboxAuthorized}"/>

  如果authenticationBean中inboxAuthorized返回是假,那么这一行标签就不用显示,多干净利索!

  先写到这里,我会继续对JSF深入比较下去,如果研究过Jdon框架的人,可能会发现,Jdon框架的jdonframework.xml中service配置和managed-bean一样都使用了依赖注射,看来对Javabean的依赖注射已经迅速地成为一种新技术象征,如果你还不了解Ioc模式,赶紧补课。

附Jsf核心教程一个JSF案例:login.rar

posted @ 2005-12-13 13:41 hopeshared 阅读(300) | 评论 (0)编辑 收藏

仅列出标题
共30页: First 上一页 22 23 24 25 26 27 28 29 30 下一页