我们知道 MDS 是 MetaData Service 的缩写; 我们也知道 MDS 跟 OAF 框架中页面显示有某种关系. 让我们来理解一下 MDS 的基础知识. Meta: 在技术领域里面, meta 表示字典. 想象一下, 一个网页被分解成许多很小的单元( 单元格, 按钮, 选项框...). 这些小单元被
我们知道 "MDS" 是 "MetaData Service" 的缩写; 我们也知道 MDS 跟 OAF 框架中页面显示有某种关系. 让我们来理解一下 MDS 的基础知识.
Meta: 在技术领域里面, meta 表示"字典". 想象一下, 一个网页被分解成许多很小的单元( 单元格, 按钮, 选项框...). 这些小单元被存储在数据库中的一个字典( 表 )里面. 当把这些单元组合起来, 就变成一个在浏览器中显示的网页.
Data: 那些字典碎片( meta pieces) 并不是以二进制文件的方式存储的, 而是存储在数据库表中. 这些表的前缀是 "jdr", 比如 JDR_ATTRIBUTES, JDR_ATTRIBUTES_TRANS, JDR_COMPONENTS 和 JDR_PATHS. 所有的字段/区域/组件( field/region/component) 的定义和关系, 都存储在这些 JDR 的表中. 当你请求一个页面的时候, OAF 框架就会从数据库里面读取这些数据. 页面的结构就是从这些 MetaData 重构出来的.
Service: MetaData 可以作为一个服务使用. 所有的数据都存在 JDR 表中, 但是所有的这些数据都必须重新连接, 所有的字段, 区域, 按钮都必须以有意义的方式组成页面. 所以可以说, MDS 提供了一项服务, 来存储/重构网页页面. MDS 服务重新把零碎的页面组件组合成有意义的页面.
在 jDeveloper 中, 当我们在创建一个页面和区域, 看起来似乎是在创建一个 XML 文件. 在OAF 框架中, 页面定义是用 XML 文件来保存的吗?
实际上页面定义是存储在 JDR 表中的, 并不是以 XML 文件存储. 但是 MDS 提供了一个 API 接口, 用来把 JDR 表数据转化成 XML 描述性文件.
因此这里有两种形式:
1. 当我们设计页面的时候, 我们把页面的定义以 XML 格式保存在文件系统里面. 当我们部署服务器/系统的时候, 我们就需要把这些 XML 格式的文件导入到 JDR 表中. OAF 框架提供了导入工具 import:
例如用下面的的命令把 RcvBarCodeLovRN.xml 导入到数据库
C:\Oracle\p9879989\jdevbin\oaext\bin\import C:\Oracle\p17888411\jdevhome\jdev\myprojects\oracle\apps\po\rcv\lov\webui\RcvBarCodeLovRN.xml -rootdir C:\Oracle\p17888411\jdevhome\jdev\myprojects -username apps -password apps -dbconnection erik-lnx.oracle.com:1522:VID
登入後複製
select * from JDR_COMPONENTS where comp_id = 'SecTxnQtyColRN';
登入後複製
2. 当用户要显示一个页面的时候, OAF 框架做了下面的事情:
第一步. OAF 框架向 MDS 请求页面定义, 这会被缓存( 第一次打开页面会比较慢, 以后会比较快).
第二步. MDS 引擎向 OAF 框架返回一个 XML 文件
第三步. XML 文件中的每一个节点/组件( mode/component) 会被转化成一个 webBean 对象.
比方说有个页面, 结构如下:
Region-Main
field1
Region-Child
Button
在这个示例中, OAF 框架将会实例化4 个 webBean 对象, 每一个对象都代表了一个组件(field/button/region...). 每一个 webBean 对象都有一些方法比如: setRendered(), setRequired(), getRequired() 等等.
第四步. 这些 webBean 对象不仅被一个个创建出来, 而且他们之间是相互嵌套的, 嵌套的顺序跟页面定义时一样. 因此页面组件的父-子关系被保留下来.
第五步. 页面准备好之后, OAF 框架会调用 MDS 页面的控制器 controller( CO.class). 当 CO 中的processRequest() 方法执行完毕之后, 用户就可以看到页面了.
到现在为止, OAF 已经显示了 web page, 下面会发生什么呢?
在回答这个问题之前, 我们先回顾一下之前的步骤. 让我们回顾一下怎样定义这个页面的. 在OAF 中, 当我们定义一个页面的时候, 有两个关键步骤:
1. 为 page 定义一个 CO/controller
2. 定义一个 AM/application module 并附加到这个页面上.
当一个页面在浏览器中显示时, 会发生下面的事情:
a. 框架会在内存中, 根据 MDS 给出的 XML 文件建立一个 webBean 的层级关系, 用来表示页面的结构.
b. 创建一个 ApplicationModule 对象. 我们可以重新获取这个 AM 对象来管理页面的数据库状态.
c. CO 中的processRequest() 方法执行.
执行processRequest() 方法会传递哪些参数呢?
OAPageContext
OAWebBean
OAPageContext 参数有什么用处?
1. 使用 oaPageContext.getParameter() 和 oaPageContext.putParameter() 来设置获取和设置字段( field) 的值.
2. 重新导向页面到当前页面或者其他页面. 比如, oaPageContext.forwardImmediatelyToCurrentPage() 重新导向到当前页面, oaPageContext.sendRedirect(newPage) 导向到其他页面.
3. 获取 AM: oaPageContext.getRootApplicationModule(), 因为 AM 已经被附加到页面上了.
4. 写 debug 信息: oaPageContext.writeDiagnostics().
5. 从 FND 消息字典中获取信息: oaPageContext.getMessage().
OAWebBean 参数有什么用?
我们知道 webBean 对象表示了页面中的结构/层次关系. 因此用这个参数, 我们可以操作这个页面结构中的所有组件. 一旦我们获得了组件的对象( field bean or button bean), 我们就可以在 runtime 的时候用 setRendered() 等等方法来改变页面的行为.
例如:
1. OAWebBean lastName = oaWebBean.findIndexedChildRecursive("personLastName");
2. 获取一个区域的对象: OAStackLayoutBean oaStackLayoutBean = (OAStackLayoutBean) oaWebBean.findIndexedChildRecursive("stackRegionName");
3. 如上面两点所示, findIndexedChildRecursive() 方法返回的对象可以被映射到对应的 bean 对象.
问题集锦:
现在有一个页面, 里面的区域是嵌套的, 每一个区域都有自己的 CO/controller. 当页面出来的时候, 每一个 CO 中的 processRequest() 方法都会被执行吗?
回答: 会的. 内部区域的 CO 会先执行, 然后执行外部区域的 CO. 这很常见, 当你在主页面中添加一个公用区域( shared region) 的时候, 每一个公用区域都可能有自己的 CO.
MDS 页面定义会被缓存吗?
回答: 会的. 因此当在数据库中 update 了MDS, 就要重启 weblogic 服务器.
但是这对用 jDeveloper 测试页面不适用, jDeveloper 会先找本地的 XML 文件/本地MDS, 只有当本地文件系统找不到, 框架才会从数据库中去找页面定义.