OAF_OAF Framework状态分析(概念)
20150706 Created By BaoXinjian
1. 架构概述
架构概述
下图显示了基本的OAF状态管理组件的应用开发视图(它并没有反映所有的OAF细节)。
2. Root Application Module(数据库会话和事务状态)
在OAF页面中,每个页面都关联到一个root application module,提供了事务上下文和JDBC数据库连接(在OAF中,数据库会话是关联到JDBC连接的)。
Root application module是OAF的中枢module,因为核心的应用数据(视图对象、实体对象等)和页面的WEB BEAN结构都自动被它(oracle.apps.fnd.framework.OADBTransaction)缓存。
警告:使用后退按钮可能使APPLICATION MODULE状态丢失。在编写代码之前请参考“支持后退按钮”。
所有存在于事务中的数据对于所有共享这一ROOT APPLICATION MODULE实例的页面都是可访问的。
OAF提供了你可以用来从事务中存储、取出、删除数据的方法。一个简单的事务可以被控制器和模型中的代码访问,这些工具是在oracle.apps.fnd.framework.webui.OAPageContext(对于控制器)和OADBTransaction(对于模型)中提供的。
1. 保持ROOT APPLICATION MODULE
默认情况下,当用户从一个页面转到另一个页面时(如一个GET请求或者一个JSP FORWARD),OAF生成一个新页面,前一个页面的application module被释放,一个新的实例从applicaiotn module池中生成。
注意:OAF从不会释放从POST请求提交的application module(除非你手工释放)。如:如果用户对一个表格排序或在表格间跳转(两个在后台提交form的动作)页面的application module实例自动被保持。
2. 保持页面间的APPLICATION MODULE
上面所说的情况适合于单独的、完整的任务。但对于由多页面组成的一个任务或者多个关联页面共用一个事务的情况,不同的页面就必须关联到一个ROOT APPLICATION实例。
达到以上的情况,你必须做到:
(1). 在多页面中为每个页面指定同一个root application module。
(2). 通过指定URL参数retainAM=Y来设置application module retention标记。
对于GET请求,这个标记是在新页面显示的时候被处理的(如上面所提到的,OAF总是为POST请求保持同一个application module,而不管retainAM参数)。
如果设为Y,前一个页面的application module会被保留,如果设为N(或者不设,视同为N),OAF会释放所有的application module,包括以前那些显式声明为保留的
你也可以在JSP FORWARD 的OAPageContext时设置此参数。
警告:只在不同的页面关联同一个root application module是不够的。如果你忘了设置retainAM标志,每个页面会使用不同的application module实例和事务,即使它们关联到了同一个application module 类型。
注意:典型情况下,根据application module池的状态,页面B 不能得到物理上的页面A的application module。但是,对象的状态会完全重置就像建立一个新的一样。你可以认为他是一个新的实例。
同样的,如果你设置了retainAM标志为Y,但是没有将多个页面关联到同一个application module,你的多个页面也会使用不同的application module和事务。
3. 有条件的保留/释放application module
在一些情况下,你可能需要决定是否要保留或释放一个application module。
你可以为不同的application module实现oracle.apps.fnd.framework.webui.OAReleaseListener接口。
警告:开发团队没有得到提醒之前,不应该使用这个接口。不正确的使用这个接口会导致内存泄漏。
4. 显式释放application moudule
有时你也会想显式的释放一个root application module,特别地,你在页面控制器中调用OAPageContext.releaseApplicationModule()方法,
OAF将会一完成页面显示就释放页面的root application module,而不用等待下一个application module请求。
3. Root Application Module保留用例
下表描述了applicatoin module保留或释放的建议
4. Servlet Session
如果在JSP基础中提到的,一个servlet session是在一系列HTTP请求的连续动作过程中在浏览器和服务器之间保持状态的的机制。
一个session可能在任意时候被应用服务器建立或通过应用、用户关闭浏览器、用户超时来终止。一个session通常对应于一个登录/退出过程,但是在OAF中并不是完全这样。
你可以在servlet session中缓存一个小的可保存的对象(OAF中必须是字符串、数字、日期),同一个session中任何页面都可以访问缓存中的数据。如:如果你认为从数据库中读取用户信息太耗资源了,你可以使用这个方法
注意:只有当你需要在不同root application module的多个页面中访问一个简单值的时候才需要使用session缓存。
你必须记住servlet session数据是没有被清除的(当SESSION激活的时候)必须被显示的清除。这样的话,如果用户没有退出而是简单的中止了,因为没有更好的事件来释放内存,故session是最后一个选择。
提示:有经验的JSP开发者可以会疑惑为什么不用隐藏域,根据OAF当前的菜单实现方法(一些菜单发送GET请求而不是POST请求),所以当你选择一个菜单里,不可能总是向请求中添加值。
如果要在SERVLET SESSION中存储、接收、删除值,查看OAPageContext put*(),get*()和remove8()方法。
5. Oracle应用用户Session
当用户登录到OAF中时,OAF建立一个AOL/J oracle.apps.fnd.common.WebAppsContext对象和基于SESSION的cookie,一起保持应用的上下文信息,如当前职责、组织ID和用户信息等。
1. Cookie保存了session中的加密的关键信息,这是数据库中的一行数据(特别的,这是一个servlet session id,在已解密的form中,是在ICX_SESSIONS表中的主键)
2. WebAppsContext从每个请求中接收键值,使用它来查询当前的session状态。
3. 用户session关联到一个servlet session,但它有自己的生命周期和超时特性。
(1). 一般情况下,用户session应该比servlet session生命周期更长。
(2). 一个用户session可能关联到多个servlet session(如果,如:当用户在建立一个时间很长的报表过程中,servlet session超时了,然后还可以在用户session超时之前继续工作)。
(3). 一个servlet session可能关联到多个用户session(如:一个用户退出,没有关闭窗口又后退)。
4. 如果用户session超时了,用户也没有关闭浏览器窗口(基于session的cookie也没丢失),也没有人删除ICX_SESSIONS表的对应记录,用户就可以在登录后从上次停止的地方继续工作。
如果你需要访问用户session中的数据,你可以OAPageContext(在控制器代码中)或者OADBTransaction(在模型代码中)。
6. 应用上下文
当不能访问OAPageContext时,你也可以使用应用上下文来存储状态(在JAVA 服务器层或者PLSQL代码中)。这样的话,你可以使用WebAppsContext.setSessionArribute(name,value)方法。
7. 页面上下文
每次接收到一个页面的请求,OAF都建立一个OAPageContext存储信息,直到新的页面完成(特别的,OAPageBean建立OAPageContext,它主要是在页面后台起做作)
8. 请求和页面边界
一个WEB应用的工作单元是一对请求/响应,浏览器发送请求,servlet处理这个请求并返回一个响应,响应的传送意味着一个请求的结束,或者是一个请求的完成与新的请求之前的分界。
同样,当OAPageBean完成页面的处理,这也是当前页面与新页面之间的分界。
所以,一个用户从页面X跳转到页面A,又跳转到页面B,这时我们有两个请求边界:X和A之间,A和B之间。也有两个页面边界:X和A,A和B。
有些情况下,请求和页面的边界不相同,考虑以下情况:
用户从X到A
在A的时候,在响应决定显示哪个页面之前,用户选择了A的控件,所以,浏览器发送了一个请求到A,当A处理结束,就达了第一个页的边界。
在页面A的代码中,开发者估算用户选择了哪个控件发送JSP FORWARD到页面B,因为我们不想重新显示A,所以不用反回HTTP响应,OAF开始处理页面B(包括建立一个新的OAPageContext),一旦B处理完成,就到达了第二个页面边界。
因为页面B必须显示给用户,故一个HTTP响应发送到浏览器,就到了请求的边界。
基于以下的原因,明白这一点是很重要的
1. 请求参数存在于请求的生命周期中,它可以跨越多个页面的边界。这对于那些认为一个请求和一个页面相同的开发者来说有点奇怪,在JSP FORWARD后也不用说明请求参数还存在。如以下示例:
(1). 用户选择了页面X中的连接,转到页面A,页面A的URL包括参数foo=bar
(2). 页面A发送一个到页面B的JSP FORWARD,现在即使我们是在一个新的页面中,请求中仍然包括foo=bar
(3). 如果你不想在jsp forward后还包括参数,你必须显式的覆盖它(注意你不能从请求中删除参数),如当你调用OAPageContext的setForward方法时你可以重置参数,foo=X
提示:将不需要的参数设为一个新值更可取,不要简单地将它设为“”。
2. 因为在页面上下文与请之前没有一对一的关系,对于从OAPageContext中访问请求参数,有人会感到疑惑。只要记得每个页面是一个明确的实体,从它的“显示点”OAPageContext表示了一个请求。
3. 当你进入上图的细节时,你会看到页面与请求的边界是明确的有不同含义的钝化事件点。
9. 请求
尽管是生命周期很短,每个HTTP请求还是建立一个对象。这个对象包括以下应用状态:
所有的URL参数,不管是POST还是GET。
如果是POST请求,包括FORM中的数据。
如果是POST请求,web bean和事件名称会关联到用户选择的控件(如用户选择了一个GO按钮,请求会包括以这个按钮命名的web bean,所以你可以知道它被按下了并以此做出响应)。
为了访问请求值,使用OAPageContext的getParameter*()方法。
10. 使用隐藏域
一个隐藏域是开发者在FORM中读取或写入数据工具,它不能被用户访问。如同用户看到的FORM域一样,隐藏域也在提交时被加到了请求中。
你可以通过在Jdeveoper中选择item style的formValue建立一个隐藏域。在运行时,它以oracle.apps.fnd.framework.webui.beans.form.OAFormValueBean来初始化。
11. 在JSP FORWARD和重定向时指定值
当你显式的使用OAPageContext setForward*()或者OAPageContext.sendRedirct()方法转到一个新的页面时,你可以设定请求参数。
例如:假定有一个页面包括一个提交按钮,当用户点击时,我们想使用JSP FORWARD转到B页面。页面A需要一传递一个mode值给页面B(可以由不同的方式来访问),所以它知道如何做
1. 用户点击提交按钮。
2. 在页面A的控制器中处理这个按钮,在processFormRequest方法中调用OAPageContext.setForwardURL()。做为一个方法调用,设定参数queryMode=automatic。
3. 在页面B的控制器中,在processRequest方法中检查参数queryMode值,调用getParameter(“queryMode”)。
4. 页面B控制器于是响应queryMode参数值为automatic,查询数据来显示页面。
12. 调用OAPageContext.putParameter()指定值
OAPageContext包括putParameter()方法,在页面处理过程中,你可以用来传递值到web bean结构中,或者使用jsp forward从一个页面传到下一个页面。
如:一个顶层的region可能放置一个值到缓存中,它的子region可以引用。
提示:这个和JAVA SERVLET 2.1的HttpServletRequest.setAttibute()方法很相似,可以认为它们做用相同。
11. 设定URL参数
当在Jdeveloper中定义URL时你可以指定请求参数,或者在相关的程序中设置URL。
警告:URL是有长度限制的,你应该小心的添加URL参数的数量,尤其是很长的时候。因为URL是可见的,你应该加密敏感的值,然后再解密。
12. 状态钝化模型(“Passivation”)
OAF是事务导向的,许多事务都跨越了多个页面。这些事务需要一定的状态来保存信息直到用户完成任务。如:一个建立采购定单的的流程中,用户在第一页描述定单,在第二页输入一个或多个项目,在第三页提交前检查信息,这个采购定单数据在每个浏览器提交的请求之间必须被保存。
HTTP协议是无状态的,它不能保存任何指定的状态也不保证支持状态保存。而且,如果提供servlet session的JVM实例失败了,或者servlet session超时了,应用状态就丢失了,等待的事务也不能恢复。
OAF,从另一方面来讲,当用户工作时,合并了保存与恢复客户端状态的能力,即使是servlet session超时(以后的版本会提供JVM失败的支持)。
1. 保存应用状态到另一个地方(在OAF中,是保存在数据库中)叫做钝化。
2. 从其它地方恢复应用状态叫做活化。
特别的,OAF目前提供了以下的状态管理特点:
可变的应用,当资源消耗很高时,OAF保存空闲线程的状态,并将它们的资源重新分配给其它线程,而不是新建一个资源实例。当空闲的线程激活时,保存的状态被恢复。简单说,资源会在JDBC连接、application modules、用户session中重新分配。
Servlet session 超时恢复,servlet session会超时,而不用用户重新开始一个未完成的事务(将来这个特点会扩展到提供中间层的失败支持)。
13. Application Module 缓冲池
为了提高性能和可变性,OAF缓冲了APPLICATION MODULE,重用是比重建更有效的方式。在简单的情况下:
1. 每个JVM都有一个application module池管理器,里面包含和管理不同的application module池。
2. 每个application module池包含了一些application module的实例,
如,如果一个应用使用了两个root application module,就有两个application module池:
oracle.apps.fnd.framework.toolbox.tutorial.server.Lession3AM和oracle.apps.fnd.framework.toolbox.tutorial.server.Lession4AM,
如下图。换句话说,每个root application module都建立一个池。
3. 在池中的application module实例被设计成可用的或者不可用的(当前被提取走的)。
4. 只有root application module是放在池中的,嵌套的application module是被root application module缓冲的。
Thanks and Regards
参考:红豆加奶 - http://shaofeng.blog.51cto.com/3392077/655672
技术交流,技术讨论,欢迎加入
Technology Blog Created By Oracle ERP - 鲍新建