| 下载 Rational® Software Architect 试用版 | 在线试用 Rational® Software Architect |
|---|
| 下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。 |
想象一下现在您加入到了一个新项目中,而您面对贴满整整一面墙上面有数以百计的表示数据实体的便利贴。您的任务就是为业务逻辑创建一个 UML 模型,以及为应用程序编写代码。当然业务逻辑建立在创建、读取、更改以及删除实体所代表的数据的基础之上。当然给应用程序编码并不会像以前那样复杂,那时 AOP 与 Spring 尚并不存在。现在由于普遍使用了例如像依赖注入(Dependency Injection,Annotations)以及面向方面编程(Aspect Orientated Programming) 这样的新概念,当初曾经流行的许多样板代码都已经消失了。因此,使用事实上已经成为标准的 Spring 是新项目中的一种需求。虽然您对这一需求很满意,但您并不准备完全实施这一方案,例如,所有的查询器都会查询每一个 Entity 属性,以及 Java™ Enterprise Edition(JEE)应用程序所有的层(layer)无法计数的各种访问方法。实现所有这些当然会花费大量的时间,而且将会是十分繁琐的。
现在,您可能会看到一点希望的火花。选择正确的工具组合,将会给您一个跳跃的起点,而且您可以立即关注业务逻辑。通过 IBM® InfoSphere™ Data Architect 与 IBM® Rational® Software Architect 还有 Skyway Builder 之间的集成,如图 1 所示,您可以得到一个强大功能的工具组合,来实现您的 JEE 开发。
图 1. 工具组合
使用这款工具组合,您可以拥有单独的模型,它们在相互之间可以转换。一个数据模型可以转换为一个应用程序模型,而应用程序模型也可以转换为一个 Skyway 的 Spring DSL。Spring DSL 是企业应用程序 Skyway 的领域专用语言(Domain Specific Language,DSL)。现在,最佳操作方式是从一个 DSL 生成代码,它要比使用像 UML 这样的目标语言更简便。Spring DSL 是 EMF 模型的规范化形式。Skyway Builder 是加速 Spring Web 应用程序开发的代码生成与构建工具。Skyway Generation Engine 满足了 MDSD 的最佳操作方式。例如,生成的代码与手工的代码之间是隔离的。包含 DSL 模型的项目隔离于引擎生成的 Web 项目。因此,您不用再去担心模型了,如果您决定继续操作而不生成的话。因为 EMF 是 Skyway Builder 的核心,所以它的 Generation Engine 是非常灵活且可以高度扩展的。
有五个可以使用的版本,包括从开放源代码到商业销售的。
- Community Edition(社区版)
- Standard Edition(标准版)
- Web Services Edition(Web 服务版)
- Professional Edition(专业版)
- Rational Software Architect Edition(RSA 版)
Skyway Builder Rational Software Architect Edition 可以与 Rational Software Architect 相集成,而且它支持 UML 到 Spring 的转换以及反向的转换。而且,它会为 UML 建模添加一个 Spring 概要文件(profile)和构造型。Skyway Builder Rational Software Architect 版本可以与 Rational Software Architect 7.5.2、7.5.3 和 7.5.4 版本一起安装。
接下来的范例,向您提供了一个更全面的视角,去理解这款工具的 MDSD 功能。
为了启用 Rational Software Architect 中的 LDM 转换,您需要对模型应用 LogicalDataModel 概要文件。安装以下的这些特性。
- 启动 IBM® Installation Manager。
- 选择 Modify 以及变更的产品(IBM® Rational® 软件交付平台)。
- 点击 Next。
- 选择 Lifecycle and architecture tool integration 主题之下的 InfoSphere Data Architect Integration,如图 2 所示。
图 2. 激活 Data Architect Integration
- 为了跟得上学习本文中所给出的范例,您需要熟悉 Spring、Spring webflow 以及 Rational Software Architect。
- 为了更好地理解,您可以参考参考资料中给出的演示视频“UML to Skyway”(10 分钟)。
- 为了对测试驱动安装 Skyway Builder 请求,请遵循安装指导。
- 在 参考资源 部分中提供了链接。
- 可以从 下载 部分中得到示例 Rational Software Architect 项目。
您可以为开始做好准备了。开始时,在 Rational Software Architect 中的 Modeling 视角下创建一个新的 UML 项目,如 Skyway Video 所示。项目被命名为 ApplicationManagement-UML。
对于 LDM 转换,开始时双击您的模型并在 Details 项上添加概要文件 LogicalDataModel(图 3)。
在接下来的部分中,您可以对 Skyway DSL 转换以及 LDM 转换应用模板。如果您不准备使用 LDM,或者已经有一个数据模型就绪,那么您就不需要从 LogicalDataModel 概要文件中来应用模式了。
图 3. 添加 Logical Data Model 概要文件
所以现在初步的工作已经完成了,您就可以继续对第一个领域对象(也叫做一个实体)进行建模了。您想要建模一个域,以通过 JMX(Java Management Extensions)来管理(Web)应用程序。因此,您需要知道应用程序的名字以管理部署应用程序的环境(产品环境或者测试环境),它们的连接参数,主机等等。
- 依据您自己的决策创建一个包结构(例如,com.ibm.de.eas.application.management.domain)
- 在 Project Explorer 中:右击模型或者包并选择:Add UML > Package
- 在每一个包中,自动创建了自由形式的图表,命名为 Main,如图 4 所示。
图 4. 创建包结构
- 双击 Main 自由图表以为您的领域类建模。为了完成这种操作,您可以从 Skyway Spring 配置板中选择
DomainObject构造型,如图 5 所示,并点击自由图。
图 5. 从 Skyway Spring 配置板中选择一个构造型
- 将您的首个领域对象(Domain Object)命名为
Application,如图 6 所示。
图 6. Domain Class Application
- 将您的鼠标移动到类框架上,然后一个用于添加属性和操作的工具栏会显示出来。
- 点击工具栏中的第一个图标以添加主属性。
- 在编辑器中输入
id:Integer,如图 7 所示,并点击 Enter。
图 7. 添加主属性
- 现在,您可以添加一个 String 属性。在编辑器中输入
name:String,如图 8 所示,并点击 Enter。
图 8:添加一个 String 属性
- 选择
id - attribute并点击 Properties 视图中的 Stereotypes 项。- 从 Spring 概要文件对 Skyway DSL 转换应用构造型
Id。 - 从 LogicalDataModel 概要文件处应用
PrimaryKey构造型,如图 9 所示。稍后到 Logical Data Model 的转换将会需要用到它。
图 9. 应用主构造型
- 从 Spring 概要文件对 Skyway DSL 转换应用构造型
- 在应用
PrimaryKey构造型之后,在属性的左边就会出现一个图标了,如图 10 所示。
图 10. 主构造型图标
- 将您的鼠标移动到类框架和箭头上,将会创建引用。点击一个箭头并拖拽到一个目标上以创建一个引用。
- 在图 11 中,您可以创建一个多对多的自我引用(self-reference)。
图 11. 创建一个关系
- 选择引用并在 Properties 视图中指定多样性。将角色名更改为
manager以及managed,如图 12 所示。
图 12. 关系角色与多样性
- 在图 11 中,您可以创建一个多对多的自我引用(self-reference)。
- 对于 LDM 转换,您必须将这个域对象标记为实体构造型。为了完成这一点,您选择类并在 Properties 视图中应用一个构造型,如图 13 所示。现在 Domain Object 的主要操作就完成了。
图 13. 应用实体构造型
- 如上所述,您完成了领域模型并添加了更多的一些属性、实体和引用(如图 14 所示)以得到比 Hello World 范例更强壮的部分。
图 14. Application-Management 领域模型
尽管对于当前示例步骤中的数据访问对象、WebController 与 Business Services 进行建模是可能的(查看 图 5. Skyway Spring 配置板)。您可以使用 Skyway Builder 的 CRUD Scaffolding 工具来生成这些类。但由于 Skyway 与 Rational Software Architect 之间的集成是双向的,您实际上可以将这些生成的类反向发送回应用程序模型中去。
领域模型现在已经为向 Skyway DSL 或者 LDM 转换做好了准备。但是在您深入研究 Skyway Builder 之前,您可以继续使用 Data Model Transformation 来为一个 IBM® DB2® 9.7 版本的数据库创建数据表。
这个部分详细介绍如何将一个 Rational Software Architect 应用程序模型向一个 InfoSphere Data Architect LDM 转换的步骤。通过一些简单的点击操作,您就能够得到 DB2 的物理数据模型以及 DDL 脚本来创建数据表。
为了进行从 UML 类图到 LDM 的转换,您需要在 Rational Software Architect 中创建一个 Transformation Configuration(回看前面 图 2. 激活 Data Architect Integration 中所示的前提条件)。
- 选择您的 UML Project 并点击
Ctrl+N以打开 Wizard Selection 对话框。 - 切换至
Transformations并双击Transformation Configuration,如图 15 所示。
图 15. 创建 Transformation Configuration
- 切换至
Data Model Transformations并选择UML to Logical Data Model - 给转换命名(图 16 中的
ApplicationManagement-Uml_TO_LogicalDataModel)并点击 Next。
图 16. 创建 UML 到 LDM 转换配置
- 选择
应用程序管理应用程序模型作为 Selected source,将ApplicationManagement-UML项目作为 Selected target,如图 17 所示。 - 点击 Next。
图 17. 指定 UML 到 LDM 转换的源与目标
- 限制字符串的长度(图 18 中的
255)。 - 点击 Finish。
图 18. 设置字符串的默认长度
- 不管何时您在更改类模型时,您都可以简单地在 Transformation Configuration 上点击 Run,来将其转换为一个 LDM,如图 19 所示。
图 19. 启动 LDM 转换
- 您可以找到 Navigator 视图中的 LDM 文件,如图 20 所示。简单地右击并复制它,然后将其粘贴到 InfoSphere Data Architect 中的 Data Design 项目中。
图 20. LDM 文件
既然逻辑数据模型已经得到了转换,您需要在 InfoSphere Data Architect 中为物理数据模型对 DB2 数据库创建一个物理数据模型。
InfoSphere Data Architect 中逻辑数据模型与物理数据模型
InfoSphere Data Architect(以前是 Rational Data Architect)是一个可视的数据建模器。这里的范例使用的版本是 7.5.2。
- 启动 InfoSphere Data Architect。
- 复制 Rational Software Architect 中的
domain.ldm文件。 - 将 LDM 文件粘贴到 InfoSphere Data Architect 中的数据设计模型中,如图 21 所示。
图 21. 导入逻辑数据模型
- 右击导入的模型并选择 Transform to Physical Data Model。
- 评审选项。将 Join Table 分隔符更改为一个下划线(“_”)并更改数据库模式(schema)的名称。
- 点击 Finish。
图 22. 转换为一个物理数据模型
- 查看物理数据模型的数据表。Join 表
APPLICATION_APPLICATION的值ID与ID1并没有得到充分的描述。根据应用程序 Self-Reference(查看 图 12. Relationship Roles 与 Multiplicity)的角色来将值- 从
ID更改为MANAGER_ID - 从
ID1更改为MANAGED_ID
图 23. 更改 join 数据表的值名
- 从
- 生成 DDL(右击并选择 Generate DDL 如图 24 所示),然后在数据库连接上的结果脚本上选择 Run SQL。
图 24. 生成 DDL 并运行 SQL
现在数据表得到了创建,您可以接着生成 Spring Web 项目了。您可以使用在下一步创建的 CRUD 应用程序来生成数据库数据表。您还可以选择使用 下载 部分提供的 DDL 脚本。
您已经创建了 Spring 与 LDM 构造型模型和实体的数据表。现在您可以使用 Skyway 的 DSL 转换,以将一个 UML 模型转换为一个支持 Spring 的动态 Java Web 项目,它包含了所有适当的 Spring 注释类与配置文件。您需要一个 UML 应用程序模型转换的 Skypeway 项目。
- 在 Project 向导中,双击
Skyway project。将项目命名为ApplicationManagement,如图 25 所示。
图 25. 创建一个 Skyway 项目
- Skyway 项目会生成一个框架 Spring Dynamic Web 项目(
ApplicationManagement-Web)以及一个企业档案 EAR(ApplicationManagement-EAR)项目。Skyway Spring DSL 模型与 Web 应用程序会得到严格的隔离,如图 26 所示。生成的代码与手工代码在物理上会得到严格的隔离。这样做遵循了 MDSD 的最佳实践。
图 26.一个空白的 Skyway 项目
- 现在您必须创建转换配置了,在上文所描述的 UML 到逻辑数据模型转换 中已经进行了描述,但是您从
Java Transformations中选择UML 到 Java Spring 的转换,如图 27 所示。转换的源是 UML 模型,而目标则是一个动态 Web 项目 ApplicationManagement-Web。
图 27.创建 UML 到 Spring Java Transformation 的转换
- 运行转换能够实现 Skyway 项目的
Spring DSL文件与来自 UML 模型的域对象。同时,Skyway 项目会将 Java 类创建到 Web 项目的generated文件夹中。
图 28. 生成的 Spring DSL Model
Skyway 的 CRUD 构建工具使您能够从一个已有的 Skyway 域对象中生成 创建,读取,更新与删除(Create, Read, Update and Delete) 功能。
- 对于每一个域对象,启动 CRUD 生成:右击并选择 Scaffolding > Generate CRUD,如图 29 所示。
图 29. 构建您的实体
- 引擎会为每一层(DAO、服务以及 Web)生成包。数据访问对象(Data Access Object)有一些搜索方法以查询数据库。在这里您必须配置数据源以解决错误(如图 30 所示),该错误与对数据库的连接有关(必须指定一种连接 )。
图 30. 构建结果
- 为了配置连接,您可以双击 DAO 并选择 Database Configuration 项。然后您可以选择一个数据源,您可以在 Database Development 视角下创建该数据源。(查看一下图 31 中的 Import Types 活动。按照一种不同的开发方法,您就能够通过简单的逆转数据库工程来创建您的域对象了)。
图 31. 配置 DAO
- 现在,让我们看一下生成
ApplicationJava 领域类的 JPA(Java 持久化 API)注释吧。您没有使用自动表创建特性,因为在这里的开发方法内,数据表已经就绪了。您必须确定关系(Joincolumns)的列得到了正确的映射。因此,您需要设置Joincolumn注释。jmxconnection关系的 JoinColumn 是JMXCONNECTIONID,如代码清单 1 所示。
清单 1. Application 类的生成 Jmxconnection Reference@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE }, fetch = FetchType.LAZY) @XmlElement(name = "", namespace = "") private Jmxconnection jmxconnection;
为了提供该信息,您可以在 Skyway 项目中执行以下操作,如图 32 所示:
- 双击 Skyway Model 中的 Application DomainObject 以变更 Persistence Mapping。
- 将 Map 更改为 ONE_TO_ONE 的区域。
- 将 Join Table 更改为 Application
- 添加名为 jmxconnectionid 的 Join Column。
图 32. JoinColumn 映射
清单 2. 更改持续性映射之后生成的 Jmxconnection 引用@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE }, fetch = FetchType.LAZY) @JoinColumns( { @JoinColumn(name = "jmxconnectionid") }) @XmlElement(name = "", namespace = "") private Jmxconnection jmxconnection;
这里,如果对所有的关系都进行这样的操作将会是令人生厌的重复性工作。所以下面您将会学习到怎样避免这种情况的发生,并会看到定制实体的生成将会是多么的容易。
代码生成基于 JET 模板。为了调整或者更改代码的生成,您可以创建一个 Skyway Template 项目(查看 图 25. 创建一个 Skyway 项目)并编辑模板。在这里您可以更改 DataType.jet 模板以编辑 Join 信息从而满足企业的习惯要求。代码清单 3 显示了在发生更改之前的模板。
DomainObject类的 JET 模板是ApplicationManagement-Templates\templates\com.skyway.integration.data.xml\templates\DataType.jet。
清单 3. 更改 JET 模板(开始之前)line 01: <c:iterate select="$model/relationships" var="relationship"> line 02: line 03: <persistence:relationshipType select="$relationship" /> line 04: line 05: <persistence:joinColumns select="$relationship" /> line 06: line 07: <persistence:joinTable select="$relationship" /> line 08: line 09: <jaxb:xmlElement select="$relationship" dataType="$model" /> line 10: private <sw:declareClassVariable select="$relationship"><java:import> <java:import><sw:javaType select="$relationship" package="true"/> </java:import></sw:declareClassVariable> line 11: </c:iterate>
- 在模型引用迭代处您要更改部件。例如,如果您有一系列的
ONE您可以书写 JoinColumn Annotation,采用关系名并添加id(查看代码清单 4 中的第 10 行代码,它显示了您在更改它之后的模板)。
清单 4. 更改 JET 模板(修改之后)line 01: <c:iterate select="$model/relationships" var="relationship"> line 02: <sw:relationshipCardinality select="$relationship" var="cardinality" /> line 03: line 04: <persistence:relationshipType select="$relationship" /> line 05: line 06: <c:if test="$cardinality = 'MANY'" > line 07: @JoinTable(name = "<sw:javaType select="$model" package="false"/>_ <sw:javaType select="$relationship/targetDataType"/>") line 08: </c:if> line 09: <c:if test="$cardinality = 'ONE'" > line 10: @JoinColumns( { @JoinColumn(name = " <sw:getVariableName select="$relationship" capitalize="false"/>id") }) line 11: </c:if> line 12: line 13: <jaxb:xmlElement select="$relationship" dataType="$model" /> line 14: private <sw:declareClassVariable select="$relationship"><java:import> <java:import><sw:javaType select="$relationship" package="true"/> </java:import></sw:declareClassVariable> line 15: </c:iterate>
- 对于给定的范例,在生成之后您就会得到一个
@JoinColumns( { @JoinColumn(name = "jmxconnectionid") }),如代码清单 5 所示。
清单 5. 更改 JET 模板之后生成的 Jmxconnection 引用@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY) @JoinColumns( { @JoinColumn(name = "jmxconnectionid") }) @XmlElement(name = "", namespace = "") private Jmxconnection jmxconnection;
- 为了实施一个新的生成,您可以删除受影响的类(在本例中,您可以删除掉包
ApplicationManagement-Web\generated\com\ibm\de\eas\application\management\domain")并清除所有的项目(选择 Project > clean)。如果您是不是感到很好奇它是否能起作用的话,那么您可以接着尝试 部署和测试 CRUD 应用程序。
如果您从 IBM Rational Software Architect 部署到 Apache Tomcat 上,那么您就需要手动管理构建路径了。查看 参考资源 部分以得到关于 Eclipse 中构建路径管理的更多信息。
启动服务器并在浏览器中输入 http://localhost:8080/ApplicationManagement-Web/indexApplication.html。您将会看到一个列出所有注册应用程序的数据表。点击 Id 列中的连接(如图 33 所示)您就会看到选择应用程序中的细节信息。
图 33. Application Overview 页面
现在您已经知道了怎样通过一系列的点击操作,来从一个 UML 模型中创建全功能的 Spring MVC Web 应用程序。在调整代码生成模板(例如为了遵循企业内部的规章制度而进行调整)时,您会享受到里面的灵活性。接下来您将会学到将您已有的代码整合到应用程序中,将会是一项多么轻松的工作。Skyway 为它提供了两种 hook。
- Hook 1:Web 应用程序的
参考资源文件夹出于这个目的考虑包含了 Spring 配置。将您的代码作为 Spring bean 添加到没有标记为生成的这些文件中,如图 34 所示。
图 34. 添加手工的 Spring beans
- Hook 2:双击 Skyway 项目的
Spring Dsl文件夹,切换至 Spring configuration 项并注册您的 Spring 配置文件,如图 35 所示。
图 35. 对 Skyway 模型注册您自己的 Spring 配置
对于 JMX 范例,您将会使用 hook 并注册如代码清单 6 所示的 ApplicationManagement-jmx-context.xml 文件。该文件包含了一些 Spring magic。手工代码 bean com.ibm.de.eas.application.management.jmx.Administrator 被暴露为一个 JMX MBean,因此像 JConsole 这样的 Administration 操控台就可以访问它了,当然 Application Management 工具也可以访问它了。您可以将手工代码(见以下的代码清单 6 到代码清单 10)放到 Web 项目的 src 文件夹中(查看 图 26. 一个空白的 Skyway 项目)。
清单 6. 文件 ApplicationManagement-jmx-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<!-- my administration mbean -->
<bean id="admin" class="com.ibm.de.eas.application.management.jmx.Administrator"/>
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<!-- will create management interface using annotation metadata -->
<bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
<!-- will pick up the ObjectName from the annotation -->
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
<bean id="AdministratorExporter"
class="org.springframework.jmx.export.MBeanExporter">
<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
<property name="exposeManagedResourceClassLoader" value="true"/>
<property name="beans">
<map>
<entry key="ApplicationManagement-Web:name=Administrator" value-ref="admin"/>
</map>
</property>
<property name="assembler" ref="assembler"/>
<property name="namingStrategy" ref="namingStrategy"/>
<property name="autodetect" value="true"/>
</bean>
</beans> |
Administrator 类演示了两个展示用例:
value属性可以通过暴露方法readValue和writeValue被一个远程应用程序(就像 JConsole)读取或者写出。- bean 是一个单例(Singleton),使用由 Skyway CRUD 构建工具所生成的 ApplicationService来从数据库中读取 Application-Domain-Objects 的。对于每一个 Application 实例,
getApplicationProxies()方法会创建一个 JMZ 代理。有了代理的帮助,您的应用程序就可以访问每一个注册的远程应用程序并读取其操作系统数据。当然您可以使用 Skyway 生成的 CRUD 应用程序来注册一个新的应用程序。
Administrator.java 文件如代码清单 7 所示。
清单 7. 文件 ApplicationManagement-Web/src/com/ibm/de/eas/application/management/jmx/Administrator.java
package com.ibm.de.eas.application.management.jmx;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;
import com.ibm.de.eas.application.management.domain.Application;
import com.ibm.de.eas.application.management.service.ApplicationService;
/**
*
* Example of a Bean which is exposed by Spring as a MBean
* The attribute value can be changed by a JMX Administration Console
*
*/
@ManagedResource(objectName = "ApplicationManagement-Web:name=Administrator",
description = "JMX Administration")
public class Administrator implements BeanFactoryAware {
private static Logger logger = Logger.getLogger(Administrator.class);
private transient Set<ApplicationProxy> applicationProxies = null;
/**
* This Service is generated with Skyways CRUD Scaffolding
* The Bean is automatically wired by Spring´s Dependency Injection
*/
@Autowired
private transient ApplicationService applicationService;
private String value = "default";
/**
* reads Application data from the database and returns JMX Proxies for each
* registered application
*
* @return Set<ApplicationProxy>
* @throws Exception
*/
public Set<ApplicationProxy> getApplicationProxies() throws Exception {
if (applicationProxies == null)
applicationProxies = new HashSet<ApplicationProxy>(1);
if (applicationProxies.size() == 0) {
Set<Application> applications;
// read the registered Applications from the database ...
applications = applicationService.loadApplications();
for (Application application : applications) {
// ... and create JMX Proxies for each application
applicationProxies.add(new ApplicationProxyImpl(application));
}
}
return applicationProxies;
}
public String getValue() {
return value;
}
/**
* Test Method, this is accessible via JMX
* @return String value
*/
@ManagedOperation(description = "readValue")
public String readValue() {
logger.debug("get value " + value);
return value;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(
* org.springframework.beans.factory.BeanFactory)
*/
@Override
public void setBeanFactory(BeanFactory bf) throws BeansException {
// make the Spring Context accessible for each application in this jvm
// not usable for production environments,
// concurrent applications would overwrite their reference
AppContext.set(bf);
}
public void setValue(String value) {
this.value = value;
}
/**
* This Method is accessible from a JMX Administration Console
*
* @param value
* @return the value changed
*/
@ManagedOperation(description = "writeValue")
@ManagedOperationParameters( { @ManagedOperationParameter(name = "value",
description = "value") })
public String writeValue(String value) {
this.value = value;
logger.debug("value " + value + " set");
return "value " + value + " set";
}
} |
代码清单 8 显示了一个接口来获取来自远程应用程序的数据。您使用这些方法来可视化 JSP 页面所服务数据表的数据。
清单 8. ApplicationProxy 接口
package com.ibm.de.eas.application.management.jmx;
import java.lang.management.OperatingSystemMXBean;
import com.ibm.de.eas.application.management.domain.Application;
/**
* JMX Proxy Interface Example.
*
* access the OperatingSystemMXBean of a remote application to read
* some data like heap size, etc.
*
*/
public interface ApplicationProxy {
public abstract OperatingSystemMXBean getOperatingSystem();
public abstract String getUrl();
public abstract String getServerUrl();
public Application getApplication();
} |
代码清单 9 中的代码是出于演示目的的简化方案。这一源代码无法用于生成环境。关于进一步的详细阐述,您可以查看 Spring Framework 参考文献。具体的解释并没有包含在本文当中。
清单 9. ApplicationProxy 的实现
package com.ibm.de.eas.application.management.jmx;
import java.io.Serializable;
import java.lang.management.OperatingSystemMXBean;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.management.MBeanServerConnection;
import org.apache.log4j.Logger;
import org.springframework.jmx.support.MBeanServerConnectionFactoryBean;
import com.ibm.de.eas.application.management.domain.Application;
/**
* Only for demonstration purposes !
*
* This class creates a JMX Proxy of a given Application Domain Object
* Internally it creates a MBeanServerConnection and registers itself and
* the Remote OperatingSystemMXBean of the Application
*/
final public class ApplicationProxyImpl implements ApplicationProxy {
/**
* wraps the jmx url for a given application
*/
final class JmxConnection implements Serializable {
private static final long serialVersionUID = 6234025617250549332L;
private Application application = null;
private Integer namingPort;
private Integer port;
private String serverUrl;
public JmxConnection(Application application) {
super();
this.port = application.getJmxconnection().getPort();
this.namingPort = application.getJmxconnection().getNamingPort();
this.application = application;
createUrl();
}
private void createUrl() {
String hostName = application.getContainer().getHost().getName();
String appName = application.getWebappName();
if (namingPort == null || namingPort == 0) {
serverUrl = "service:jmx:rmi:///jndi/rmi://" + hostName + ":"
+ port + "/jmxrmi";
} else {
serverUrl = "service:jmx:rmi://" + hostName + ":" + namingPort
+ "/jndi/rmi://" + hostName + ":" + port + "/"
+ appName;
}
}
public Integer getPort() {
return port;
}
public String getServerUrl() {
return serverUrl;
}
@Override
public String toString() {
return getServerUrl();
}
}
private static Logger logger = Logger.getLogger(ApplicationProxyImpl.class);
private static final String OPS = "ops";
private Application application = null;
/**
* Spring ID MBeanServer
*/
private String idMBeanServer = null;
private JmxConnection jmxConnection = null;
private Map<String, Object> proxies = new HashMap<String, Object>();
private String url = null;
public ApplicationProxyImpl(Application application) {
super();
this.application = application;
String host = application.getContainer().getHost().getName();
String app = application.getName();
idMBeanServer = host + app;
url = "http://" + host + ":" + application.getPort() + "/"
+ application.getWebappName();
jmxConnection = new JmxConnection(application);
}
private void createMBeanServerConnection() {
try {
if (jmxConnection != null) {
String surl = jmxConnection.getServerUrl();
Properties p = new Properties();
p.put("serviceUrl", surl);
MBeanServerConnection conn = (MBeanServerConnection) AppContext
.registerBean(idMBeanServer,
MBeanServerConnectionFactoryBean.class, p);
logger.info("JMX MBean Server registered to " + idMBeanServer
+ " with " + surl);
registerProxies(conn);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
public Application getApplication() {
return application;
}
/*
* (non-Javadoc)
*
* @see com.ibm.de.eas.application.management.jmx.ApplicationProxy#
* getOperatingSystemMBean()
*/
public OperatingSystemMXBean getOperatingSystem() {
// create MBean and
// register ops to spring container
return (OperatingSystemMXBean) getProxies().get(OPS);
}
private Map<String, Object> getProxies() {
if (proxies.size() == 0) {
try {
createMBeanServerConnection();
} catch (Exception e) {
logger.warn("Problem with JMX Connection: " + e.getMessage());
proxies.clear();
}
}
return proxies;
}
public String getServerUrl() {
return jmxConnection.toString();
}
public String getUrl() {
return url;
}
/**
* register MBean Proxy to the Spring DI-Container
*/
private void registerProxies(MBeanServerConnection mbeanServerConnection) {
String id = OPS;
Object proxy = AppContext.registerMBean(id + idMBeanServer,
"java.lang:type=OperatingSystem", OperatingSystemMXBean.class,
mbeanServerConnection);
proxies.put(id, proxy);
}
} |
代码清单 10 显示了 Spring 注册 helper 类。
清单 10. Spring registration helper 类
package com.ibm.de.eas.application.management.jmx;
import java.util.Enumeration;
import java.util.Properties;
import javax.management.MBeanServerConnection;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.jmx.access.MBeanProxyFactoryBean;
public class AppContext {
static BeanFactory beanFactory = null;
private static Logger logger = Logger.getLogger(AppContext.class);
public static BeanFactory get() {
return beanFactory;
}
public static void set(BeanFactory bf) {
beanFactory = bf;
}
/**
* This method uses Spring to register and create MBeans dynamically
*
* @param id - Spring Context id
* @param objectname - the name of the MBean
* @param proxyInterface - the interface of the MBean
* @param mbeanServerConnection - the connection to access the MBean
* @return - the instance of the MBean
*/
@SuppressWarnings("unchecked")
public static Object registerMBean(String id, String objectname,
Class proxyInterface, MBeanServerConnection mbeanServerConnection) {
try {
Properties props = new Properties();
props.put("objectName", objectname);
props.put("proxyInterface", proxyInterface);
props.put("server", mbeanServerConnection);
registerBean(id, MBeanProxyFactoryBean.class, props);
return AppContext.get().getBean(id);
} catch (Throwable t) {
logger.warn("Error creating the MBean Proxy " + objectname
+ ": " + t.getMessage(), t);
}
return null;
}
/**
*
* Registers a bean to the Spring Context at runtime
*
* @param id - Spring Context id
* @param clazz - Class to register to Spring
* @param props - Properties to set to the Instance of clazz
* @return
*/
@SuppressWarnings("unchecked")
public static Object registerBean(String id, Class clazz, Properties props) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(clazz);
Enumeration<Object> keys = props.keys();
while (keys.hasMoreElements()) {
Object k = (Object) keys.nextElement();
builder.addPropertyValue(k.toString(), props.get(k));
}
((BeanDefinitionRegistry) AppContext.get()).registerBeanDefinition(id,
builder.getBeanDefinition());
return get().getBean(id);
}
} |
既然您的 JMX 基础已经为运行做好准备了,那么最终您就需要创建一个视图以查看管理应用程序中的数据。您要为 Spring Web Flow(SWF)使用 Skyway 的可视化编辑器来创建一个简单的 Master Detail 用户流程。SWF 用于为在多个请求下发生的用户交互进行建模。
- 首先,在 Skyway 项目中创建一个模型包,然后使用 Web Flow 向导来创建一个 webflow(右击并选择 New > Web Flow),如图 36 所示。
- 将 webflow 命名为
admin。 - Web Flow 向导将会询问放置 SWF 模型(
admin.xml)的位置。将其保存到ApplicationManagement-Web\WebContent\WEB-INF\flows。
图 36. 创建一个 Spring Webflow
- 接下来,通过简单地将
States和Transitions拖拽到工作区来构建您的 webflow。- 将您的后端 bean 定义为一个 webflow 变量。
- 点击
Flow configuration element上的加号(“+”) - 插入变量名(
adminView) - 插入您的 View 类的名字(
com.ibm.de.eas.application.management.flow.admin.Admin)
com.ibm.de.eas.application.management.flow.admin.Admin,或者从带有adminView表达式的 webflow 来访问。 - 点击
- 准备 webflow 的第一部分以进行测试。
- 拖拽
View state并将其命名为admin。 - 使用一个
transition来将Flow 配置元素与View state联系起来。 - 将转换命名为
admin。
admin事件(URL 或者按钮点击),那么adminwebflow 将会进入到视图状态admin中。通过使用 admin.jsp,因为您不能为该视图状态指定任何页面。 - 拖拽
- 对另一个视图状态执行相同的操作。
- 拖拽另一个
View state并将其命名为viewOperatingSystemDetail。 - 使用
transition来将Flow 配置元素与View State viewOperatingSystemDetail联系起来。 - 将转换命名为
showOperatingSystemDetail。
showOperatingSystemDetail(URL 或者按钮点击),那么adminwebflow 将会切换至 View 状态viewOperatingSystemDetail。因为您并没有为该 View 状态指定任何的页面,按照惯例用户将会被指向 viewOperatingSystemDetail.jsp 。 - 拖拽另一个
- 您按照下面方式,来返回到 admin View 状态:
- 定义一个
End state并给它起一个名字(例如,goBack)。 - 使用一次转换将 View state 与 End state 联系起来。
- 给 Transition 命名(例如,命名为
goBack)。
- 定义一个
图 37. 创建一个 Spring webflow
现在您可以查看为视图建模得来的代码。Skyway 会生成如代码清单 11 所示的
admin.xml,一个是 adminView state的 JSP,一个是第二个View state的 JSP。
清单11. Spring Webflow 模型 admin.xml<?xml version="1.0" encoding="UTF-8"?> <flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/webflow" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> <var class="com.ibm.de.eas.application.management.flow.admin.Admin" name="adminView"/> <view-state id="admin"/> <view-state id="viewOperatingSystemDetail"> <transition on="goBack" to="goBack"/> </view-state> <end-state id="goBack"/> <global-transitions> <transition on="admin" to="admin"/> <transition on="showOperatingSystemDetail" to="viewOperatingSystemDetail"/> </global-transitions> </flow>
- 将您的后端 bean 定义为一个 webflow 变量。
- 现在您已经手动创建了由流程所定义并由 JSP 页面所引用的视图类。通过访问
getRemote()方法,JSP 页面能够呈现每一个持续性呈现的操作性数据。对管理性 MBean 的引用由 Spring 的依赖注入(Dependency Injection)(@Autowired注释)来实现,如代码清单 12 所示。
清单 12. 查看 Bean com.ibm.de.eas.application.management.flow.admin.Adminpackage com.ibm.de.eas.application.management.flow.admin; import java.io.Serializable; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import com.ibm.de.eas.application.management.jmx.Administrator; import com.ibm.de.eas.application.management.jmx.ApplicationProxy; public class Admin implements Serializable { /** * */ private static final long serialVersionUID = -486243002544361404L; @Autowired @Qualifier("admin") private transient Administrator administrator; public String getValue(){ return administrator.getValue(); } public void setValue(String value){ administrator.setValue(value); } public Set<ApplicationProxy> getRemote() throws Exception { return administrator.getApplicationProxies(); } }
- 正如前面所定义的那样,您就可以访问 JSP 页面中
adminView的视图类。为了访问属性(或者方法),使用像${adminView.value}这样的表达式。在 JSP 规范 V2.0 及后续版本中,表达式语言会得到整合。该表达式会产生一种getValue()方法的调用。为了在getRemote()方法的结果进行迭代,您可以使用一个迭代变量(例如,current),它的类型是 ApplicationProxy。为了访问 ApplicationProxy 关系的属性,您可以使用像${current.application.name}这样的表达式。向具体视图输入转换的按钮如下所示:<input type="submit" name="_eventId_showOperatingSystemDetail" value="Details"/>.showOperatingSystemDetail是您进行建模以输入视图状态viewOperatingSystemDetail的转换。代码清单 13 演示这几点。
清单 13. 改善 admin.jsp<%@ page language="java" isELIgnored="false" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <jsp:directive.include file="/WEB-INF/sitemesh-decorators/include.jsp"/> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Administration</title> <skyway:javascript src="prototype.js"></skyway:javascript> <skyway:javascript src="skyway.js"></skyway:javascript> <skyway:javascript src="skyway-internal.js"></skyway:javascript> </head> <body> <skyway:form action="${flowExecutionUrl}" commandName=""> <skyway:label value="${adminView.value}"></skyway:label> <br><br> <table id="applicationTable"> <thead> <tr> <th>ID</th> <th>Application</th> <th>Console Url</th> <th>Jmx Url</th> </tr> </thead> <tbody> <c:forEach items="${adminView.remote}" var="current" varStatus="i"> <c:choose> <c:when test="${(i.count) % 2 == 0}"> <c:set var="rowclass" value="tableRow1"/> </c:when> <c:otherwise> <c:set var="rowclass" value="tableRow2"/> </c:otherwise> </c:choose> <tr class="${rowclass}"> <td>${current.application.id}</td> <td>${current.application.name}</td> <td>${current.url}</td> <td>${current.serverUrl}</td> </tr> </c:forEach> </tbody> </table> <input type="submit" name="_eventId_showOperatingSystemDetail" value="Details" /> </skyway:form> </body> </html>
- 为了显示细节信息,如代码清单 14 所示,您可以按照前面所述的方法来使用视图类,但是要访问
OperatingSystem与MBean方法:${current.operatingSystem.systemLoadAverage}。
清单 14. 改善 viewOperatingSystemDetail.jsp<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <jsp:directive.include file="/WEB-INF/sitemesh-decorators/include.jsp"/> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Operating System Details</title> </head> <body> <skyway:form action="${flowExecutionUrl}" commandName=""> <skyway:label value="Operating System Details"></skyway:label><br><br> <table id="opsTable"> <thead> <tr> <th>Application</th> <th>Name</th> <th>Version</th> <th>AvailProc</th> <th>Load</th> <th>Arch</th> </tr> </thead> <tbody> <c:forEach items="${adminView.remote}" var="current" varStatus="i"> <c:choose> <c:when test="${(i.count) % 2 == 0}"> <c:set var="rowclass" value="tableRow1"/> </c:when> <c:otherwise> <c:set var="rowclass" value="tableRow2"/> </c:otherwise> </c:choose> <tr class="${rowclass}"> <td>${current.application.id}:${current.application.name}</td> <td>${current.operatingSystem.name}</td> <td>${current.operatingSystem.version}</td> <td>${current.operatingSystem.availableProcessors}</td> <td>${current.operatingSystem.systemLoadAverage}</td> <td>${current.operatingSystem.arch}</td> </tr> </c:forEach> </tbody> </table> <input type="submit" name="_eventId_goBack" value="Back" /> </skyway:form> </body> </html>
打开服务器的启动配置然后为手工代码部分配置 JMX Mbean 服务器。添加如代码清单 15 所示的代码:
清单 15. JVM 参数以激活 JMX
-Dcom.sun.management.jmxremote.port=9001 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false |
启动服务器并插入 http://localhost:8080/ApplicationManagement-Web/admin。URL 中的 admin 是您在之前建模的转换。有了这个 URL,admin SWF 就会启动并处理 admin.jsp 了,如图 38 所示。
图 38. Admin 视图
当您点击 Details 按钮时,会输入 viewOperatingSystemDetail 视图状态,而相应的 JSP 会呈现给用户,如图 39 所示。
图 39. Operating Systems 视图
在本文中,您学习了怎么样基于模型转换来生成一个全功能状态的 Spring 应用程序。您将一个应用程序模型转换为逻辑数据模型,并最终创建了数据库数据表。然后您将应用程序模型转换为 Spring DSL,以在短时期内构建 Spring Web 应用程序。您可以通过 Skyway 的可视编辑器而查看 JPA 实体表映射所发生的更改。您学到了怎样通过更改 JET 代码生成模板来达到相同的效果。然后,您会学到将生成的代码与手工创建的代码隔离开来,以及向项目添加手工代码的方式。最后您使用用于 Spring webflow 建模的 Visual Builder 来对一个引用遗留代码的用户交互进行建模。
Rational Software Architect、InfoSphere Data Architect 与 Skyway 工具集合能够极大地帮助您简化并促进软件的交付过程。它使得您可以将注意力放在模型上,从而避免更多地关注实施方面的细节问题。因此所节省的时间,尤其对于开发大型的应用程序而言,是相当可观的。如果您是 Java 和 Spring 的初学者,那么您拥有了一个稳固、设计良好且容易扩展的基本 Spring Web 应用程序。基于 Eclipse EMF,您可以扩展 Spring DSL 模型来满足您未来所面对的复杂需求。
现在,您会选择 Skyway 了吗?
感谢 Jürgen Herrmann 博士 和 Martin Braun 对本文所做的评审与评论工作。感谢 Klaus Schlöter 博士和 SDC 团队对 Skyway 实时对话部分所做的重大回馈。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| RSA 模型1 | ApplicationManagement-UML.zip | 23KB | HTTP |
| JET 模板2 | ApplicationManagement-Templates.zip | 4KB | HTTP |
| Java、JSP、Spring 配置源码3 | ApplicationManagement-Web.zip | 12KB | HTTP |
| DDL 脚本4 | ddl.zip | 2KB | HTTP |
注意:
- 完整的 Rational Software Architect 建模项目,可以作为一个 Project Interchange File
- 创建一个 Skyway Template 项目并将该 zip 文件的内容粘贴到项目中
- 手工代码:将该 zip 的内容粘贴到生成的 Web 项目中
- 所生成的 DDL 和会产生数据表的脚本
学习
-
Spring Framework:仔细查看参考手册文档
-
CRUD 定义:了解 Create Read Update Delete:四个基本持久功能
-
用 Eclipse Modeling Framework 实现模型驱动开发,第 1 部分 -- 创建 UML 模型并生成代码(developerWorks,2004 年 4 月)创建 UML 模型并产生代码。
-
查看模型驱动软件开发的最佳实践
-
到 Skyway 软件下载 Skyway Builder
-
观看视频:UML To Skyway
-
查看 Rational Software Architect 的 Skyway Builder 安装指南
-
理解 Eclipse 中的 Build Path Management
-
请求 Skyway Builder Rational Software Architect Edition Test Drive
- 查看 Rational Software Architect 和 Infosphere Data Architect 的资源。
- 查看“集成 Rational Software Architect 和 Rational Data Architect”以及“集成 Rational Software Architect 和 InfoSphere Data Architect”(developerWorks,2009 年 4 月)将您的应用模型和数据模型一起工作。
-
理解 Java Persistence Api
-
查看 Spring Webflow 参考手册
- 了解更多有关内容:Rational Software Architect for WebSphere Software:
- Rational Software Architect for WebSphere Software信息中心
- developerWorks Rational Software Architect for WebSphere Software 产品专题
- Rational Software Architect for WebSphere Software 试用版下载
- Rational 开发工具讨论区
- 在 IBM Rational 软件交付平台 中了解其它应用程序,包括适用于并行开发和地域分布式团队的协作工具,以及用于架构管理、资产管理、变更和发布管理,集成需求管理、过程和组合管理,和质量管理。您可以在 IBM Rational 在线文档中心 查找产品手册、安装指南以及其它文档。
- 访问 developerWorks 上的 Rational 专区,了解有关 Rational 软件交付平台产品的技术资源和最佳实践。
- 查找 Rational 基于计算机、基于 Web,以及在线指导课程。训练您的技能,并学习更多有关 Rational 工具的课程,包括入门级和高级课程。在此目录上的课程可进行购买,包括基于计算机的和基于 Web 的培训。此外,一些“入门”课程是免费的。
- 订阅 IBM developerWorks 时事通讯,获得有关最佳的 developerWorks 教程、文章、下载、社区活动、网络广播和事件的每周更新。
- 浏览 技术书店,获得有关这些和其它技术主题的书籍。
获得产品和技术
- 下载 IBM Rational 软件的试用版本。
- 下载这些 IBM 产品评估版,并着手使用来自于 DB2®、Lotus®、Tivoli®,以及 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 参与论坛讨论。
- 查看 developerWorks 博客,并加入 developerWorks 中文社区。
