EBS OAF 页面的技术剖析(2)
(版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处;否则请与本人联系,违者必究)
原文来自于OAF开发文档
OADBTransaction
图5:基础模型架构-OADBTransaction
注意:要完全的精确和兼容,这个图应该包含实现类oracle.apps.fnd.framework.server.OADBTransactionImpl而不是oracle.apps.fnd.framework.OADBTransaction接口,尽管如此,我们选择稍后再包含它因为你只在你的代码中使用这个接口。
就像上面图中展示的,OADBTransaction在你的模型代码扮演了中心角色,因为它封装了关联根应用模块的JDBC连接/数据库会话,并且直接拥有所有你创建的实体对象(你的由根应用模块拥有的视图对象,在他们视图行上持有的是它们实体对象的引用)。你也会在你的模型代码中为下面的通常行为正常使用OADBTransaction:
l 创建一个可调用的语句来执行PL/SQL方法或程序。
l 访问会话级别应用上下文信息比如用户名,ID,当前职责等等。
l 如果你需要进行NLS操作比如把服务器日期/时间转换为用户日期/时间等等,可以访问oracle.apps.fnd.framework.OANLSServices对象。
对OADBTransaction的访问是由根应用模块所提供的。
界面视图
界面视图格式化并展示模型数据给用户。
第三章的实现界面视图里详细介绍了下面的内容。
定义页面
在开发期间,你可以使用声明式JDeveloper工具为每个页面指明bean的层次结构,JDeveloper在Building “Hello, World”里有介绍。在Oracle E-Buisness Suite开发中,你会使用(源控制)XML文件来进行页面定义。当你的产品部署到一个客户的站点时,OAF框架会在数据库存储之外运行这些页面定义。
简介概括来说,你可以使用JDeveloper来定义由区域和items组成的页面.
l Items是简单的小部件就像按钮,字段,图像等等不包含子控件的小控件。
l 区域是容器对象,它可以包含items和其它的区域。区域是示例比如headers, tables和特殊的布局组件。
l 每个你定义的区域和Item都有一个style属性,它会告诉OAF框架如何在运行时怎么去实例化这个web bean(这个也表明了为这个bean要生成什么HTML)。比如,如果你定义了一个区域它的style属性为”table”,那么OAF会实例化一个oracle.apps.fnd.framework.webui.beans.table.OATableBean对象。
l 所有的页面必须有一个顶层的区域(一般称为”根区域”),它的style为pageLayout,会被实例化为oracle.apps.fnd.framework.webui.beans.layout.OAPageLayoutBean对象。
l 在JDeveloper页面树(和相应的XML文件)中区域和items的顺序会告诉OAF框架把这些对象加到运行时bean组织层次中哪个地方。
下面的图6给了一个实例化的示例页面的各种web beans的幕后外观。你看的的标签是后面的web beans的名称。比如,一个弹出式列表实例化为oracle.apps.fnd.framework.webui.beans.message.OAMessageChoiceBean,一个提交按钮实例化为oracle.apps.fnd.framework.webui.beans.form.OASubmitButtonBean.
图7展示了相应的页面定义。
图6:UI组件页面展示了相应web beans的名称
注意:下面显示的区域和item名称不符合Oracle EBS命名标准;反而,它们是用于帮助你解释相应的结构到相应的web beans.
图7:JDeveloper中页面结构
属性集
每个区域或者item都可以使用属性集来继承属性组的设置。一个属性集时一个命名的,可重用的属性集合,它可以别任何类型的UI对象属性,包括区域,item和其它的属性集。每当你建立了一个使用了属性集的UI,你可以覆盖继承的属性(虽然在OAF编程标准中是不鼓励的)。
要演示这个概念,在应用开发中,每个表必须为每个显示列关联属性集。这些属性集包含提示,显示宽度等等属性。
l 在OAF ToolBox Sample Library/Tutorial中,有一个采购订单表(FWK_TBX_PO_HEADERS),其主键列类型为NUMBER且名为HEADER_ID,它也作为采购订单号显示给用户。
l 这个表有一个叫做关联的FwkTbxPoHeaders属性集XML包文件,它包含了表中所有显示列的属性集(每个列都有一个属性集)。其中之一的属性集叫做HeaderId。
l HeaderId属性集有一个Prmpt属性设置为Order Number并且显示长度设置为合理的15.
l 当你创建一个包含采购订单编码Item的页面时,我们可以设置它的属性集属性为完全合适的名为/oracle/apps/fnd/framework/toolbox/attributesets/FwkTbxPoheaders/Headerid的属性集。
图8:在JDeveloper中使用一个属性集
组件重用
组件重用
如果你想包含共享对象到你的页面中,你可以简单继承它们。
比如,在OAFToolBox Sample Library/Tutorial中,我们创建了一个通用区域(名为PoSummaryRN),因此同样的内容不需要编码就可以包含在多个页面中。要加一个共享区域到一个页面,我们只需要简单创建一个新的区域,然后设置它的Extends属性为共享区域的符合条件的全名称: /oracle/apps/fnd/framework/toolbox/tutorial/webui/PoSummaryRN
注意:在引用页面,共享区域是不可以编辑的,因此它的items在JDeveloper结构面板中都是灰色的。
图9:在JDeveloper中扩展一个区域
数据源绑定
对于任何需要和数据库交互的beans(查询,插入,更新和/或者删除),你也需要指定一个绑定到View Instance Name数据源并且关联View Attribute Name.这个绑定至关重要,因为OAF框架使用它从潜在的视图对象中获取查询数据或者写用户输入的数据到视图对象实例中。
l View Instance Name属性引用它所包含的应用模块上下文中的潜在的视图对象(所有的视图对象都”存活”于一个应用模块中并且在它的容器内由实例名称来标示)。比如,如果一个SupplierVO视图对象在你页面的根应用模块中由实例名称”MySupVO”标示,”MySupVO”这里就是你指定的名称。
l View Attribute Name引用了映射到列的潜在视图对象中的属性。比如,如果你有的SupplierVO有一个”SupplierId”属性(它映射到了潜在的SUPPLIER_ID列),“SupplierId”在这里就是你指定的名称。
定义菜单
所有的OAF应用就像Oracle Browser Look and Feel (BLAF) UIGuideline: Tabs/Navigation.描述的都包含菜单。你可以使用Oracle EBS的菜单和功能定义forms声明式的定义这些菜单结构。稍后我们会在开发文档中详细介绍这个。
就像OAF会翻译你的声明式UI布局到运行时的bean层次结构,它也包含了声明式的菜单定义的web beans.
定义页面流
当处理多页面事务流时,OAF框架为复杂的硬编码的控制器逻辑提供了一个声明式(因此可客制化)的方法。关于这个功能的额外信息可以参考Chapter 4: Declarative Pageflow UsingWorkflow。
个性化页面
OAF框架也包含了一个声明式的叫做OA Personalization Framework客制化的基础设施.这是为了终端用户和产品发布链支持客制化的相应(更改本地化,排列等等)。
注意:就像你在开发文档中看到的,声明式的创建区域和item比编程创建它们更好。实际上,你仅应该当你不能声明式的创建它们时才通过编程式创建组件,这样客户可以个性化你的工作。
控制器
控制器响应用户的动作并指引应用流转。
第三章中的Implementing the Controller文档更详细的描述了下面的内容。
控制器可以在区域层次上界面视图进行关联(从更普遍的角度来看,任何实现了oracle.apps.fnd.framework.webui.beans.OAWebBeanContainer接口的OAF web beans都可以关联控制器)。
所有你创建的控制器都像下面图10中展示的继承于oracle.apps.fnd.framework.webui.OAControllerImpl。
控制器类是你定义webbeans如何表现的类。特别的是,你写控制器代码用于:
l 在运行时操作/初始化UI(包含任何编程实现的而不能通过声明式实现的布局)
l 拦截和处理比如按钮按下的用户事件。
请求处理
当浏览器为你的页面发出一个OA.jsp请求时:
1. oracle.apps.fnd.framework.webui.OAPageBean(主要的OAF页面处理类)使用页面名称来决定它需要哪个应用模块,因此它可以从应用模块池中取出其应用模块实例。这个应用模块也会从连接池取出一个JDBC连接,并且页面的事务上下文也会建立起来。
2. 验证用户会话;如果无效,会显示登录页面(注意这是为了简单化;更多详细信息会在后面的开发文档中有提及)。
3. 假设用户是有效的,OAPageBean会根据请求参数来判断正处理的是HTTP POST还是GET请求。
处理GET请求
当浏览器发出一个GET请求到服务器来请求一个页面时(或者你手工转向它),OAF框架会使用声明的UI定义来构建web bean的层次结构:
1. OAPageBean会调用页面顶层pageLayout bean的processRequest()方法,然后整个web bean层次结构都会如下被递归处理来初始web beans(包括任何关联的模型组件):
1. 每个web bean实例化它的控制器—如果它有的话—并调用控制器上的processRequest(OAPageContextpageContext, OAWebBean webBean)方法。这个方法是你用于构建/修改你页面布局,设置webbean属性和做任何手工数据初始化(如果,比如,当你导向到一个页面时,你需要做自动查询)的方法。
2. 一些复杂的webbeans(像oracle.apps.fnd.framework.webui.beans.table.OATableBean和oracle.apps.fnd.framework.webui.beans.layout.OAPageLayoutBean)需要通过调用他们的prepareForRendering()方法来做post-controller处理(这个方法在相应的bean Javadoc里有详细描述)。
3. 每个web bean调用它的子控件的processRequest()方法。
2. oracle.apps.fnd.framework.webui.OAPageBean把web bean的层次结构交给UIX来生成页面并发送给浏览器。
处理POST请求
当浏览器为一个页面向服务器发出一个POST请求时:
1. OAPageBean检查是否web bean层次结构已在内存中。如果没有(因为资源已经被回收,用户通过浏览器的回退按钮导航,或者从一个消息对话框页面发出一个POST请求到主页面),它会就像在上面GET请求处理中描述的一样来重新创建层次结构。
2. OAPageBean调用在层次结构中的所有beans的processFormData(OAPageContextpageContext, OAWebBean webBean)方法来把form数据写回到模型中(特别的,它会调用pageLayout区域上的processFormData()方法,然后每个web bean递归调用它的子控件上的processFormData()方法)。写回form 数据到对应的模型会自动调用属性和实体级别的验证,并且如果你抛出任何验证异常,处理就会终止并且错误消息会显示给用户。
3. 如果在processFormData()阶段没有抛出异常,OAPageBean就会使用上面描述的方式来调用在层次结构中的web bean的processFormRequest(OAPageContextpageContext, OAWebBean webBean)方法。这样你的控制器代码就有机会来对用户的动作作出相应的处理了。
4. 如果没有JSP转向或者页面重定向请求发出--或者异常在processFormRequest()抛出—那么页面就会被刷新。
OAPageContext
当OAF框架收到一个OA.jsp请求时,OAPageBean会创建一个oracle.apps.fnd.framework.webui.OAPageContext对象,这个类仅存在于页面的处理期间。上面描述的3个重要方法(processRequest(), processFormData()和processFormRequest())的每一个都使用OAPageContext作为参数,并且任何你写的控制器代码都会利用这个重要的类。
图10:OAPageContext类和其它关键类的关系
就像上面的图演示的,OAPageContext有对请求和根应用模块的引用。有了这些关系,OAPageContext被传递给每个你的控制器响应处理的方法,你就可以知道如何在下面列出的常用任务使用OAPageContext:
访问请求参数
可能最重要的,这是你用于读取请求参数值的类,通过调用一个简单的方getParameter(Stringname)法(记住请求包含全部URL参数加上—如果是一个POST请求—全部form字段值,加上用户选择的关联动作/控制部件的名称和事件)。
小技巧:对于你的页面上的单个web beans(按钮,字段等等),传递给getParameter()的name的值是相应的你在定义页面时分配的标识ID。因此,比如,你可以在控制器里写下面的代码阿狸知道是否用户按了你在JDeveloper命名为”GoButton”的按钮
processFormRequest(OAPageContextpageContext, OAWebBean webBean) { if (pageContext.getParameter("GoButton") != null) { // The user pressed the "Go" button, do something... } }
访问根应用模块
OAPageContext缓存了根应用模块的引用,其可以提供对视图对象和事务的访问。如果你需要访问一个应用模块,可以像下面:
processFormRequest(OAPageContext pageContext, OAWebBean webBean) { OAApplicationModule am = (OAApplicationModule)pageContext.getRootApplicationModule(); }
发出导航指令
你可以使用这个类的方法来告诉OAF框架来进行JSP转向或者一个客户端的重定向。比如(我们会在稍后的开发文档中对这个方法做更详细的介绍):
processFormRequest(OAPageContext pageContext, OAWebBean webBean) { if (pageContext.getParameter("CreateButton") != null) { // The user pressed the "Create Supplier" button, now perform a JSP forward to // the "Create Supplier" page. pageContext.setForwardURL("OA.jsp?page=/oracle/apps/dem/employee/webui/EmpDetailsPG", null, OAWebBeanConstants.KEEP_MENU_CONTEXT, null, null, true, // Retain AM OAWebBeanConstants.ADD_BREAD_CRUMB_YES, // Show breadcrumbs OAWebBeanConstants.IGNORE_MESSAGES); } }
访问应用上下文信息
就像你的模型代码中的OADBTransaction,OAPageContext提供了对servlet会话层Oracle EBS上下文信息,比如用户名称,id,当前职责等等。比如,下面的代码片段演示了如何获取用户名称:
processRequest(OAPageContext pageContext, OAWebBean webBean) { String userName = pageContext.getUserName(); }