显示标签为“ADF”的博文。显示所有博文
显示标签为“ADF”的博文。显示所有博文

2009年5月10日星期日

Oracle Application Development Framework 简介

1.1    概述

    Oracle Application Development Framework是Oracle Fusion中间件体系架构的重要组成部分,是一个端到端的J2EE开发框架。ADF框架通过提供实用级别的基础架构服务和虚拟的声明式开发体验,从而简化了J2EE开发。不同于其他开源开发框架,Oracle ADF提供了一个包含Business Service、Model、View、Controller的完整MVC实现,在企业应用架构的每一个层次都提供了支持。

1.2    Oracle ADF 11g框架简介

    ADF架构图:
    如架构图所示,Oracle ADF 11g分别提供了MVC架构中每一层次的实现,并允许在不同层次使用不同的技术。如可以使用ADF Business Components构件Business Service层,使用structs构建Controller层等。这里主要描述使用Oracle ADF对MVC框架的实现构建,即Business Service层使用ADF Business Component,Model层使用ADF Model,Controller层使用JSF,View层使用JSF/ADF Faces。

1.2.1    View 层

    最简单的View层实现即为一个包含用户界面组件的页面,并允许该页面中的组件调用Model层中的方法。Oracle ADF为了提高View层的重用程度,引入了如下结构体系。
  • Page
    使用JDeveloper构建的Page即为最终向用户展现的Web 页面,其为Oracle 富客户端组件构建的基础。
  • Page Fragment
    Page Fragment的创建与使用与Page极其相似,对于开发人员来说,在JDeveloper中开发一个Page与开发一个Page Fragment基本没有什么区别。Page与Page Fragment的主要区别在于,Page Fragment并不是最终向用户展现的页面,它可以作为一个Page的组件所存在。在通常的开发场景下,常遇到部分页面的某部分布局和业务逻辑固定不变的情况。在ADF中可将其开发为一个Page Fragment,并将其作为页面组件放入所需Page中,以提高页面的可重用程度。
    得益于Page Fragment的可重用特性,在常见的应用场景中,并不需要为一个业务流程创建一组页面,而只需创建针对该业务流程的一个页面以及与其相对的一组Page Fragment即可。
  • Page Template
    Page Template可以用于构建Page 或 Page Fragment,并可将其上放置的ADF富客户端组件与Model/Controller绑定以执行业务逻辑。Page Template主要用于用于快速构建一组具有相同部分的页面。Oracle ADF开发人员可以创建一个模板,并基于模板创建Page或Page Fragment。
    在常的应用场景下,可以通过Page Template构建某应用的页眉、页脚、导航栏等,而将页面内容的构筑交予其他开发人员,进一步降低开发耦合度,提高团队协作水平。
  • Region
    Region是Fusion架构体系中新引入的概念,可以被应用于Page或Page Fragment。可以将Page Fragment或Task Flow作为Region放入Page中,而Page Fragment、Task Flow、目标Page将分别使用独立的上下文配置。
    常见的使用场景是使用Region构建页面内容,以实现导航菜单与页面内容的松耦合设计。
  • Contextual Event Framework
    Oracle ADF为实现View的松耦合设计,允许通过Context对页面富客户端组件添加绑定,以调用Controller或Model(Bussiness Service Application Model)中的方法,实现MVC架构中的调用关系。这种基于上下文的事件处理架构即称为Contextual Event Framework。
  • ADF富客户端组件
    在Fusion架构体系中,ADF提供了超过100种富客户端组件供开发人员在Page或Page Fragment上创建富用户交互界面。所有组件均基于JSF及AJAX技术构建。包括:
    • 页面布局控件
    • 表/树控件
    • List of Value控件
    • 输入控件
    • 导航控件
    • 输出控件
    • 查询控件
    • 数据图表组件

1.2.2    Controller层

ADF Controller是ADF应用的重要组成部分,用于控制用户交互及程序流的执行。其核心为Task Flow,包含如下内容:
  • Task Flow
    Task Flow任务流供开发人员定义一组Page或Page Fragment之间的流程及逻辑关系。Task Flow中的对象可视为一组活动(activities),如每一个加入Task Flow中的Page或Page Fragment均被视为一个视图活动(View activity)。当用户在一个Task Flow中的各个页面间跳转时,即可视为在各视图活动之间的跳转。亦可在Task Flow中添加其它类型的活动。Oracle ADF已经将Task Flow中的各种活动包装为组件供用户调用。
    Task Flow在Oracle ADF中包含两种类型的实现:Bounded Task Flow/Unbounded Task Flow。
  • Bounded Task Flow
    Bounded Task Flow是Oracle ADF框架支持的两种Task Flow实现之一。该类型的Task Flow支持事务处理和作为Region重用,具体具有如下特性:
    • 定义明确的事务处理边界;
    • 单个Task Flow入口及零至多个定义明确的Task Flow出口;
    • 支持PageFlow级别的变量传递,可以将定义的变量在Task Flow的各个活动间传递;
    • 允许为该Task Flow创建新的实例;
    • 允许在退出该Task Flow之前执行commit或rollback的操作;
    • 当该类型Task Flow被其他Task Flow调用时,允许在进入该Task Flow时传入参数作为PageFlow级别的变量;
    • 当该类型Task Flow被其他Task Flow调用时,允许在退出该Task Flow时返回变量。
  • Unbounded Task Flow
    Unbounded Task Flow与Bounded Task Flow的主要区别在于Unbounded Task Flow没有定义明确的边界,不支持将某活动定义为Task Flow的入口,亦不可将其作为Page的Region使用。每一个ADF应用中只包含一个Unbounded Task Flow。典型的应用情境是使用Unbounded Task Flow构造导航栏,调用Bounded Task Flow。
    注意,若一个ADF应用中定义了多个Unbounded Task Flow文件,编译时会对Unbounded Task Flow进行合并。若一个Unbounded Task Flow中的视图活动(View activity)被设置为bookmarkable时,则可通过URL直接访问这个视图活动。
  • Task Flow Template
    Task Flow Template提供了重用一个或多个Bounded Task Flow的方法。这里的重用包含两种机制:复制(Copy)和引用(reference)。

1.2.3    Model层

    Model层提供了访问源数据及业务逻辑的功能。在Fusion架构中,将源数据的访问与业务逻辑的访问分为两部分:Data Control与Business Service。Data Control使用Databinding文件连接View层与Business Service/源数据,是Model层的主要组成部分;Business Service主要分为Application Module和ADF Business Components,通过Data Control连接至View层。这种分离式的架构允许在View层已经创建的前提下为View层添加数据访问或Business Service访问。
  • Model
    • Data Control
      Data Control用于为Page或Page Fragment创建数据访问界限(Data Bounds),可以实现为诸如XML文件、Web Service或ADF业务组件。使用JDeveloper进行开发,当创建Page或Page Fragment时,会自动创建对应的Data Control。
    • Placeholder Data Control
      当进行Page或Page Fragment的UI开发时专用的Data Control,用于在界面建立后连接数据源。
  • Business Service
    在Fusion体系架构中,ADF业务组件(ADF Business Component)是主要的数据访问和业务逻辑来源。ADF业务组件,亦称为ADF BC,主要由以下几个部分组成:
    • Application Module
      Application Module(AM)定义了一组可更新的数据模型及顶层的可调用方法(亦称为Service Methods),是用户界面进行数据操作的接口。AM中包含View Object对象。
      为了简化应用集成的复杂度,可以为Application Module启用服务接口。这里的服务接口指的是一组允许第三方应用程序使用标准Web Service协议完成查找、创建、更新、删除业务数据的任务的可编程API。所有的业务验证规则均被封装在Entity Object中,并会在进行业务数据操作时自动进行验证。
    • View Object
      View Object是一条SQL查询的持久化对象,允许对SQL查询的结果进行操作。同时,View Object间允许通过建立连接(link)的方式创建主从(Master-Detail)结构。当用户从用户界面改变数据时,View Object会将数据改变传递至Entity Object触发对应验证规则和保存改变。
    • Entity Object
      Entity Object是数据库中某张表的某个行的持久化对象。Entity Object内部通过DML操作将数据改变反映至数据库。Entity Object支持通过建立Association的方式进行Entity Object之间的关联,并支持通过此方式反映数据源层次的表关系,如数据表的外键关联。


2009年4月27日星期一

ADF Validator

    实际应用中经常遇到对业务相关的数据进行Validate的情况。比如对输入数据进行格式限制/范围限制等。ADF为开发人员提供了相对灵活的解决方案。这里提供了几种Validator的不同实现方式。

    (下述均使用Oracle XE自带的HR示例中Employees表作为示例表)
  1. 使用EO层次的Validator

    首先,ADF框架中所有的Validator默认都使用EO级别的Validator。EO层次可以设置Entity级别的Validator或Attributes级别的Validator。

    选择相应的级别,点击界面上的加号即可创建Validator,ADF提供了灵活的Validator以供选择


    选择需要的Validator Rule Type,写入相应的验证规则后,即可在Failure Handling中写入验证失败的抛出消息。在此处设置的消息将写入资源文件中。


    最后,当VO被拖入页面时,对应控件将自动包含一个<f:validator>子控件,其绑定为EO层次的Validator。


  2. 在ManageBean中实现Validator

    ADF本身提供的Validator相对比较灵活,但当EO的重用程度比较高时(如不同的VO使用相同的EO,需要设置不同验证规则/不同弹出消息,甚至不同页面使用相同VO,需要不同验证规则),EO级别的Validator显然不能满足要求。则可以通过重写Validator并添加至页面控件的绑定以实现。
    <f:validator>绑定的对象为一个属性,所以首先应为该Validator创建一个实现Validator接口的Class,最简单的如下(可以写入更复杂的验证逻辑)

    public class testValidator implements Validator {
    public void validate(FacesContext context, UIComponent component,
    Object obj) throws ValidatorException {
    throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_WARN, "TEST", "TEST MESG"));
    }
    }

    在MB中创建一个该属性的实例,并构造相应的get/set方法如下

        private testValidator test = new testValidator();

    public void setTest(testValidator test){
    this.test = test;
    }

    public testValidator getTest(){
    return this.test;
    }

    最后,通过EL表达式将该属性绑定至<f:validator>

    <f:validator bindings="#{backing_ExpsProYbudgetUpdate.test}">


2009年4月24日星期五

Oracle ADF VO排序及VO的查询模式

    常规应用中,当需要使用Table向终端用户展示数据时,Table中数据的显示排序一致性极大程度的影响到了客户体验。通常希望诸如多次查询结果显示顺序相同、插入数据在原数据上方等的实现。

    ADF为开发人员提供了两种级别的排序,即数据库级别的排序及内存级别(In-Memory)的排序。需要同时使用这两种排序法,才能取得合适的排序效果。
    (使用Oracle示例数据库HR中的Employees表作为示例)

  1. 数据源排序
    编辑VO的Query,设置Order by字段,这里设置Manager作为排序字段
    需要注意的是,这里的排序只是针对从数据源选取数据时的排序,当对VO进行插入/删除操作时,修改并不会立即Commit至数据库,而是存储在EO/VO中,这就导致这种排序法对于不在数据库中存在实体行的数据是无效的,也是需要使用In-Memory排序方式的原因。


  2. VO的查询模式(View Object's SQL Mode)
    在开始VO的In-Memory排序之前,首先应该了解VO的查询模式。
    ADF中一个VO具有如下查询模式
    • ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES
      VO的默认查询模式,该模式下VO每次executeQuery时会从数据库检索数据

    • ViewObject.QUERY_MODE_SCAN_VIEW_ROWS
      检索已经存在于VO的Row set中的数据,允许在此查询模式下使用VO的In-Memory Filtering

    • ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS
      检索存储于EO缓存中的数据

    可以使用setQueryMode()方法进行VO查询模式的设置。可以单独设置一个查询模式或使用Java的OR(|)设置多个查询模式。例如
    setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES | ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS)

    当设置多个查询模式时,会自动跳过重复行。
    设置过查询模式后,当执行executeQuery()方法时,查询模式的设置即可生效。


  3. In-Mamory排序
    使用setSortBy()方法进行针对VO的运行时In-Memory排序。setSortBy()方法的输入参数类似于SQL中的order by子句,不同的是将列名称替换为VO上的Attribute Name。例如
    setSortBy("ManagerId desc, EmployeeId");

    根据查询模式的设置不同,当执行executeQuery()时,setSortBy()会自动转换为相应的排序方法。如当使用数据库检索时,会将setSortBy转换为ORDER BY子句,当使用In-Memory查询时,会将setSortBy转换为SortCriteria对象。
    使用示例:
    vo.setSortBy("ManagerId");
    vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS);
    vo.executeQuery();

    只需要在如插入数据时执行该语句,即可完成In-Memory的排序功能。


  4. 自定义VO的排序方法
    当需要的排序方法比较复杂,使用setSortBy设置排序规则无法满足需求时,可以通过重写VO的public void sortRows(Row[] rows)方法和public Comparator getRowComparator()方法达成目的。其中sortRows方法即是进行In-Memory排序时执行的排序方法;getRowComparater方法返回一个Comparator类型对象,被VO的compareTo()方法调用。

2009年4月20日星期一

Weblogic开启关闭


概述
近日由于项目需要,花了些时间部署了我们开发的demo环境到客户公司,客户公司使用的是IBM的AIX操作系统,由于是ADF应用,应用服务器也就毫无疑问地选择了Weblogic。而在应用部署过程中,总结了一些比较有用的Weblogic启动和关闭的脚本。

实现
1、使用相关Telnet工具登录到远程服务器,创建将要发布应用的domain,这里名为demo_domain。
2、正常启动Weblogic:
cd到/bea/user_projects/domains/demo_domain/bin
使用命令./startWebLogic.sh启动Weblogic
此时,Weblogic可以正常启动,但是当使用ctrl+c或关闭当前窗口后,Weblogic即立刻停止,显然在正式应用中这样的方式一般来说是不符合要求的。
3、后台启动(以服务方式启动)Weblogic:
为解决上面描述的问题,需要在后台启动Weblogic,也就是经常提到的以服务的方式启动Weblogic,将信息输出到文件中,这里输出到demo.log。
#nohup ./startWebLogic.sh >demo.log 2>&1 &
在执行上面的命令后,将返回一个进程号,可以记录该进程号,当Weblogic无法正常关闭时,可以通过命令行结束进程。
4、一般来说,启动Weblogic时一般需要输入用户名和密码,在这里假定用户名和密码都是weblogic。当后台启动Weblogic时,输出信息到提示输入用户名时即停止,无法继续正常开启。为解决这样的问题,可以通过修改相应的启动脚本。在这里,我修改了bin目录下的setDomainEnv.sh,添加了如下代码:
WLS_USER=weblogic
WLS_PW=weblogic
JAVA_OPTIONS=-Dfile.encoding=UTF-8
5、停止Weblogic:
./stopWebLogic.sh
有时使用此命令无法正常停止Weblogic,经测试,这种现象还是经常发生的,在出现该状况时需要强行停止该进程。
首先使用ps –ef|grep java查看当前进程,找到该进程,当然,如果刚才已经记录了进程编号,这里就无需再进行查询。
查到后使用 kill -9 进程编号 停止该进程。
有的操作系统用线程模拟进程,部分操作系统下面,Weblogic进程变现为多线程,需要用脚本杀死Weblogic多线程。
ps –ef|grep java |awk ‘{printf “kill -9 ”$2}’>killjava
sh killjava 就可以停掉Weblogic的java进程,但是需要注意该机器是否还有其他java进程,切记小心停错!

参考:http://cyr1974.itpub.net/post/2066/247321
http://liwp_stephen.itpub.net/post/3553/24829
http://www.lupaworld.com/bbs/thread-14112-1-1.html

ADF实现树状查询

最近项目遇到要用start with connect by做树状查询,即:选择一个部门老大,要把老大下面的小老大和各个小弟全查出来,ADF貌似没有提供这类查询(至少我没发现,呵呵,如果有那就更好了,可以不给下面看了),这个办法不一定最好,但是,绝对可以实现,~~
1.数据库里面表的结构示例:
user(userid username PanrentId)
2.建VO的sql
2_1)如果你能知道你要查询的最顶层user的userid,那么你可以这样建VO,绑定一个参数p_userId,默认值就是最顶层的user的userid;VO上面的sql为:
select * from user start with userid=:p_userId connect by prior parent_id = user_id
2_2)如果你不知道你要查询的最顶层user的userid,那么你可以这样建VO,绑定一个参数p_userId,默认值就是最顶层的user的userid;VO上面的sql为:
select * from user start with userid in (select userid from (select userid, 1 flag from users union all select :p_userId, 2 flag from dual) where flag = decode(:p_userId, null, 1, 2) ) connect by prior parent_id = user_id

这样,如果用户没有输入查询参数,查出来的就是全部;如果用户输入了查询参数,把用户输入的userid在manageBena或者vo实现类里面赋值给VO上面的绑定参数

2009年4月18日星期六

实现ADF应用集成

概述
本文主要描述在ADF开发中如果实现不同应用之间的集成。
场景:
在开发过程中,为了降低开发难度和风险,有时会采用在基础框架应用外新建应用进行开发,待新应用开发完成后,再将其集成到基础框架应用中。这里将以howtoappsintegration1和howtoappsintegration2两个应用为例,说明如何实现系统之间的集成。

实现
1、分别创建新应用howtoappsintegration1和howtoappsintegration2,两个应用都有Model项目和ViewController项目。
2、我们将howtoappsintegration2应用集成到howtoappsintegration1应用中。在howtoappsintegration1应用中打开howtoappsintegration2应用的Model项目,这里名为Model2。此时,在howtoappsintegration1应用中,应该包含Model,Model2和ViewController三个项目,由于在一个应用中只能包含一个ViewController项目(多个ViewController项目Runtime时会报异常)。
3、修改howtoappsintegration1中的ViewController项目的Dependencies,同时依赖于Model和Model2两个项目,在Data Control面板中可以看到包含两个Data Control。



4、在完成了上述步骤后,我们就可以开始将ViewController项目进行合并。打开两个应用所在的文件夹,首先拷贝howtoappsintegration2应用的task flow到howtoappsintegration1的对应目录下(一般task flow存在于public_html中的WEB-INF文件夹中),在这里为app2-task-flow.xml。
5、在Jdeveloper中刷新Application Navigator面板,可以看到该task flow已经存在于对应目录下,打开该task flow。发现该task flow中的页面包含叉号,说明页面出错或不存在,这里是由于不存在,点开该task flow的source,查看该页面在howtoappsintegration2应用的存放目录。






<?xml version="1.0" encoding="UTF-8" ?>


<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">


<task-flow-definition id="app2-task-flow">


<default-activity>Jobs</default-activity>


<view id="Jobs">


<page>/Jobs.jspx</page>


</view>


</task-flow-definition>

</adfc-config>


6、通过步骤5我们发现该页面直接存在于其public_html目录下,拷贝该页面到howtoappsintegration1应用中,在这里也拷贝到了public_html目录下。完成后刷新Jdeveloper的刷新Application Navigator面板,此时再次查看app2-task-flow.xml的source,发现错误消失。
7、在howtoappsintegration1应用中打开上步中拷进来的页面,即Jobs.jspx,点击其Source,发现有非常多的警告,查看后发现,确实了页面的绑定信息,即缺少了页面对应的PageDef文件(如果页面使用了模板,还需要根据实际情况修改模板信息或删除模板)。
8、找到howtoappsintegration2应用中Jobs.jspx页面绑定文件,一般在adfmsrc目录下,这里名为JobsPageDef.xml,将其拷贝到howtoappsintegration1应用的对应目录下。完成后刷新Jdeveloper的刷新Application Navigator面板,此时再次查看Jobs.jspx页面的source,发现警告没有消失,此时虽然PageDef已经存在于该应用中,但是由于没有在DataBindings.cpx中指定页面和页面绑定信息的关联信息,因此页面仍然无法找到其绑定信息。
9、在howtoappsintegration1应用中打开DataBindings.cpx文件,我们直接修改其Source。需要修改的地方主要有:
(1)dataControlUsages:定义dataControl信息
(2)pageDefinitionUsages:定义页面绑定信息
(3)pageMap:定义页面和页面绑定的关联信息
在这里将该文件进行如下修改:







<?xml version="1.0" encoding="UTF-8" ?>


<Application xmlns="http://xmlns.oracle.com/adfm/application" version="11.1.1.51.88" id="DataBindings"


SeparateXMLFiles="false" Package="howto.howtoappsintegration1.view" ClientType="Generic">


<pageMap>


<page path="/Employees.jspx" usageId="howto_howtoappsintegration1_view_EmployeesPageDef"/>


<page path="/Jobs.jspx" usageId="howto_howtoappsintegration1_view_JobsPageDef"/>


</pageMap>


<pageDefinitionUsages>


<page id="howto_howtoappsintegration1_view_EmployeesPageDef"


path="howto.howtoappsintegration1.view.pageDefs.EmployeesPageDef"/>


<page id="howto_howtoappsintegration1_view_JobsPageDef"


path="howto.howtoappsintegration1.view.pageDefs.JobsPageDef"/>


</pageDefinitionUsages>


<dataControlUsages>


<BC4JDataControl id="AppModuleDataControl" Package="howto.howtoappsintegration1.model.services"


FactoryClass="oracle.adf.model.bc4j.DataControlFactoryImpl" SupportsTransactions="true"


SupportsFindMode="true" SupportsRangesize="true" SupportsResetState="true"


SupportsSortCollection="true" Configuration="AppModuleLocal" syncMode="Immediate"


xmlns="http://xmlns.oracle.com/adfm/datacontrol"/>


<BC4JDataControl id="AppModuleDataControl1" Package="howto.howtoappsintegration2.model.services"


FactoryClass="oracle.adf.model.bc4j.DataControlFactoryImpl" SupportsTransactions="true"


SupportsFindMode="true" SupportsRangesize="true" SupportsResetState="true"


SupportsSortCollection="true" Configuration="AppModuleLocal" syncMode="Immediate"


xmlns="http://xmlns.oracle.com/adfm/datacontrol"/>


</dataControlUsages>

</Application>


10、保存,在howtoappsintegration1应用中再次打开Jobs.jspx页面,点击查看source,发现页面绑定信息已经找到(或者通过点击Binding,通过关联链接进而点击查看PageDef信息)。



11、虽然页面文件已经找到,但是在页面的source中仍然存在一些警告信息,这是由于该页面中的属性绑定到了backing scope的managed bean(或称为backingbean)上,此时此managed bean尚未存在于该应用中。根据source中managed bean的名称在howtoappsintegration2应用中的adfc-config.xml中找到该managed bean的路径和名称,将其拷贝到howtoappsintegration1应用的对应目录下(一般存在于src目录下),此时刷新Application Navigator后,查看到该managed bean,务必选中该manged bean,右键点击rebuild,否则在下步中将有可能无法看到该类(如果package的路径不同,有可能需要打开代码手动修改该路径,修改后重新rebuild)。

12、完成后将其按照source中的名字定义到howtoappsintegration1应用中的adfc-config.xml中,这里名为backing_Jobs,Scope为backing scope。



13、此时再次查看howtoappsintegration1应用中的Jobs.jspx的source,发现绑定到managed bean中属性的信息上的警告消失,此时所有拷贝和修改已经基本完成。
14、保存应用,运行拷贝进来的task flow,进行测试。

最终运行页面:




总结:
应用集成是一个看似容易却相对琐碎和比较容易出问题的环节,所以在进行应用集成的过程中,尽量按照步骤,按部就按地进行,切忌将所有的内容全部拷贝然后再去修改配置,如果是这样的话,出了问题就将非常难以排查,尤其是在需要多人协同开发的项目上(这里建议的最佳实践就是由专人负责应用集成)。一般而言,只要细心,按照步骤进行,是不会有问题的。下面是在集成过程中需要特别注意的几点:
(1)文件一定要拷贝完整
(2)注意library是否正确,数据库连接是否正确
(3)DataBinding.cpx文件修改一定要小心,根据需要注册Data Control到该文件
(4)Managed Bean拷贝完成后一定要注意其Package路径,并进行rebuild
(5)在最终集成到的应用中,一定只能包含一个ViewController项目(Model项目可以有多个)。
(6)如果task flow中有用到了method call,有可能要根据实际情况重新拖拽。


相关代码请查看:
howtoappsintegration.rar

将ADF应用部署到Weblogic


概述
在Jdeveloper 11g中,Oracle已经将Weblogic集成到了IDE中,在使用Jdeveloper 11g进行ADF的开发时,Weblogic也是其默认的部署和运行服务器。同时,在实际应用中,有很多情况下,也需要将ADF部署到Weblogic上,本文主要描述在ADF开发中如何将ADF应用部署到Weblogc。
约定:
1、 Weblogic的域已经存在或使用Jdeveloper默认的Weblogic的域
2、 Weblogic的域在创建时,在“选择域源”步骤中,在“生成一个自动部署的域以支持下列产品”项中勾选了“Application Development Framework”。
3、 将要发布到Weblogic上的应用已经成功创建并完成。
下面我们将部署howtodeploytoweblogic应用到weblogic,这里使用Jdeveloper默认的域。这里仅为部署步骤简介,详细的说明参见文档(Page963 b31974.pdf 第32章 )。

实现
1、 启动Weblogic。如果是通过Jdeveloper打开,可以点击Run-Start Server Instance,若不是通过Jdeveloper打开,可以通过运行脚本。
2、 选择Model项目,双击打开Application Module,点击Configurations,继续选中Local的Configuration,将Connection Type设置成为JDBC DataSource,选择后Datasource Name为java:comp/env/jdbc/howtoDS。



3、 选择ViewController项目,双击点开Project Setting,选择Deployment,删除已经存在的Deployment Profiles,点击New,选择Archive Type为War File,Name为test。



4、 创建WAR后,在Edit War Deployment Profile Properties页面中,选择Web Application’s Context Root为Specify Java EE Context Root,设置为test,点击OK完成。



5、 在应用上点击右键,选择New,在Deployment Profiles的Items中,可以看到只有EAR可用,选择EAR,命名为howtodeploytoweblogic。



6、 在Edit Ear Deployment Profile Properties页面中,选择Application Assembly,选中ViewController.jpr下的test,点击OK完成。



7、 删除原有的Ear,确保此时Deployment Profiles中只有howtodeploytoweblogic,点击OK完成。



8、 在应用上点击右键,点击Deploy,选择howtodeploytoweblogic,进一步选择to EAR file。



9、 发布完成以后记录此时EAR包所在路径,同时登陆Weblogic的管理页面,这里使用Jdeveloper默认域,用户名和密码均为weblogic,url为http://127.0.0.1:7101/console/
10、登录成功后,首先为应用创建数据源,在域结构中点击 服务-JDBC-数据源,选择新建,进入数据源向导。
10.1、点击新建,名称为howtodeploytoweblogic,JNDI的设置需要和步骤2中的Datasource保持一致,这里为jdbc/howtoDS,数据库类型选择Oracle,驱动使用Oracle’s Driver(Thin) Versions:9.0.1,9.2.0,10,11。
10.2、点击下一步到连接属性设置,完成数据库连接属性,点击下一步显示所有的连接属性,点击测试配置,确定连接测试成功。
10.3、继续点击下一步到选择目标,勾选将要应用该数据源的服务器,然后点击完成,消息提示“已激活更改,不需要重新启动”。



10.4、选中刚创建成功的数据源,点击 配置-连接缓冲池,选择高级,勾选保留时测试连接项,继续选择监视,测试数据源,若测试成功,说明数据源配置成功。至此,数据源配置完成。



11、登录成功后在域结构中点击部署,此时在右边可以看到所有已部署在服务器上的应用和库。点击安装,进入安装向导。
11.1、输入刚才EAR包的路径,选中该EAR。



11.2、点击下一步,Weblogic会自动判断将要部署的内容为应用还是库,这里为应用。
11.3、继续点击下一步,根据需要进行设置,这里使用默认设置,直接点击完成。提示部署成功,即可通过浏览器进行访问。url为http://127.0.0.1:7101/test/faces/Main.jspx需要注意的是,这里url地址中的test,对应的是在设置war profiles中的Web Context Root。


最终运行结果:


相关代码请查看:
howtodeploytoweblogic.rar

2009年4月17日星期五

复合LOV的实现


概述

在实际应用中,经常会用到对于同一个属性使用不同LOV,本技巧主要介绍了对于同一属性使用多个LOV的方法,以及不同LOV之间切换的方法。
场景:
1、 定义新员工时,根据员工学历的不同,使用不同LOV选择起薪范围
2、 根据职位不同,LOV中显示的内容也不同或需使用不同LOV显示内容
3、 其他应用场景
下面我们以EmployeesVO中的DepartmentID属性为例,根据员工Salary的不同为该属性添加使用不同LOV,以显示不同内容。

实现
1、创建新应用后,创建Entity Object:EmployeesEO,该EO基于数据库中的Employees表,可以看到其关联(EmpManagerFkAssoc)被自动创建。
2、创建基于1中创建的EO的View Object:EmployeesVO。
3、创建名为EmpManagerFkLink的View Link,在“Selected Source Attribute”和“Selected Destination Attribute”中都选择EmployeesVO中的EmpManagerFkAssoc,点击“OK”完成。



4、创建DepartmentId的LOV数据源VO(DepartmentLOV),并设置DepartmentId属性为Key Attibute,其SQL如下:
SELECT Departments.DEPARTMENT_ID,
Departments.DEPARTMENT_NAME,
Departments.MANAGER_ID,
Departments.LOCATION_ID
FROM DEPARTMENTS Departments


5、为EmployeesVO中的DepartmentId属性添加两个LOV,其List Data Source均选择为前面创建的DepartmentsLOV(DepartmentsLOV1),List Attribute为DepartmentId,List Return Values使用默认,即DepartmentId-DepartmentId,在UI Hints中设置不同的显示项,分别如下:
LOV_DepartmentId:



LOV_DepartmentId1:



6、为DepartmentId属性添加LOV Swicher,单击选中该属性,在“List of Values: DepartmentId”中设置List Type UI Hint为Input Text with List of Values,在List of Values Switcher点击新建,命名为lovSwitcher,在“Edit Attribute: lovSwitcher”面板中选择Value Type为Expression,添加Value内容,即Groovy脚本,以判断使用哪个LOV,在这里根据Salary进行判定(若Salary>10000,使用LOV_DepartmentId1,若Salary<=10000,使用LOV_DepartmentId)。
Value:
if(Salary != null && (new oracle.jbo.domain.Number(Salary)).intValue() > 10000)
{
return 'LOV_DepartmentId1'
}
return 'LOV_DepartmentId'



7、新建页面,命名为Main,为方便扩展功能,产生出了其Backing Scope的Managed Bean。
7.1、选中并点开“Data Controls”中“AppModuleDataControl”内的EmployeesVO2(与AM中名字相同,不一定为该名),继续点击“Named Criteria”,将All Queriable Attributes拖动到页面中,选择ADF Query Panel with Table,根据需要调整显示属性,完成后确定。



7.2、在“Structure”面板中选择af:table – redId1,双击打开其属性编辑器,选择“Columns”项设置所有属性(除lovSwitcher)的“Component To Use”为输入类型控件。
7.3、保存设置,运行页面。


最终运行结果:
在Search区中在Salary选项中填入9999(<10000),DepartmentId此时使用的LOV是LOV_DepartmentId,显示DepartmentId和DepartmentName两项。


更改Salary为10001,重新查看DepartmentId,发现其显示列数分别为DepartmentId,DepartmentName与ManageId,使用的LOV为LOV_DepartmentId1。


清空Search中内容,使用默认空值进行查询,在Table中继续验证。


相关代码请查看:
howtomultiplelov.rar

ADF 11g Multiple RowSelect on RichTable

在实际应用中,经常遇到需要在表格中选定一条或多条记录进行操作的情况。
  1. 使用ADF RichTable提供的Multiple RowSelect功能

  2. 在RichTable中增加列,使用基于VO的Checkbox进行RichTable的行多选

  3. 在RichTable中增加列,不基于VO进行RichTable的行多选

下面将详述三种实现方式,使用Oracle JDeveloper 11.1.1.0.1,数据源使用OracleXE版本中HR示例数据的Employees表,创建页面时默认绑定backingbean。

1.使用ADF RichTable提供的Multiple RowSelect
优点:RichTable原生支持,稳定快速,代码简单
缺点:只能按住键盘Ctrl键或Shift键通过鼠标进行多选,用户操作不直观
步骤:设置RichTable的属性,将RowSelection设为multiple,并将SelectedRowKeys设为空,即可在后台通过如下代码获得选定行的RowKey,进而可以通过在VO上通过对应RowKey得到对应行。

    public Boolean getIsChecked() {
//return (Boolean) getAttributeInternal(ISCHECKED);
return getAttributeInternal(ISCHECKED) == null ? false :
(Boolean)getAttributeInternal(ISCHECKED);
}


补充:为什么将SelectedRowKeys置为空
修改之前SelectedRowKeys值为#{bindings.EmployeesVO1.collectionModel.selectedRow},该EL表达式表明之前该Table的SelectedRowKeys绑定到了对应VO的selectedRow,但client端的状态改变并不能实时的提交到Server端,以至于当此绑定存在时,取到的是Server端存储的selectedRow而非client端当前的selectedrow。
2.在RichTable中增加列,使用基于VO的Checkbox进行RichTable的行多选
优点:使用checkbox的方式提供选择,代码复杂度中等
缺点:必须修改每一个需要使用该多选方式的RichTable对应的VO
步骤:
  1. 在VO添加非数据库项IsChecked,设置其Type为Boolean,Updateable为Always


  2. 为该VO生成ViewRowImpl.java,并修改IsChecked的get方法

        public Boolean getIsChecked() {
    //return (Boolean) getAttributeInternal(ISCHECKED);
    return getAttributeInternal(ISCHECKED) == null ? false :
    (Boolean)getAttributeInternal(ISCHECKED);
    }


  3. 将VO拖入页面创建RichTable,修改IsChecked列,将标签改为<af:selectBooleanCheckbox>,并将其Autosubmit/Immediate属性设为true,并在header中拖入另一个SelectBooleanCheckbox


  4. header里的Checkbox不能添加ValueChangeListener,如果已经添加,需要将ValueChangeListener重置为Default(空)。为Header里的Checkbox创建ClientListener和ServerListener,分别设置属性如下:
    ClientListener
    method: mainClientCall
    type: click

    ServerListener
    method: callMBmainmethod
    type: onSelectedOrDeselected()

    完成后如下





  5. 在页面转入Source模式,在标签 <af:document... <af:messages... 间加入如下代码

          <script type="text/javascript">
    <![CDATA[function mainClientCall (event)
    {
    var currcheckbox = event.getSource();
    var selectstatus = currcheckbox.getValue();
    //call method in manage bean
    AdfCustomEvent.queue(currcheckbox, "callMBmainmethod",{params:selectstatus}, true);
    }
    ]]>
    </script>




  6. 在页面backingbean中修改onSelectedOrDeselected方法,添加以下代码以实现全选功能。注意修改this.getTable()为你的目标表。
        public void onSelectedOrDeselected(ClientEvent clientEvent) {
    boolean isSelected =
    (Boolean)clientEvent.getParameters().get("params");
    RichTable rt = this.getTable1();
    for (int i = 0; i < rt.getRowCount(); i++) {
    JUCtrlHierNodeBinding rowData =
    (JUCtrlHierNodeBinding)rt.getRowData(i);
    Row row = rowData.getRow();
    if (isSelected) {
    row.setAttribute("IsChecked", true);
    } else {
    row.setAttribute("IsChecked", false);
    }
    }
    // Refresh target table state
    AdfFacesContext adfContext = AdfFacesContext.getCurrentInstance();
    UIComponent refreshComponent = this.getTable1();
    if (refreshComponent != null) {
    adfContext.addPartialTarget(refreshComponent);
    }
    }


  7. 可以在AppModel或ManageBean中通过遍历VO判断IsChecked的状态来取出选定行。


3.在RichTable中增加列,不基于VO进行RichTable的行多选
优点:使用checkbox的方式提供选择,
不必修改VO
缺点:
代码复杂度较高
步骤:

  1. RichTable中插入列,设置宽度(width)为13RowHeader属性为trueAlign属性为Center;在列及列header中分别拖入SelectBooleanCheckBox,设置checkboxsimple属性为true,删除其Text属性中的值。将全选checkboxID设置为maincheckbox,单选的checkbox
    ID
    设置为subcheckbox:(参考2.3)

  2. 分别为两个Checkbox创建一组ClientListener/ServerListener,属性设置如下(参考2.4)
    MainCheckbox
    clientListener
    Method: mainClientCall
    Type: click
    serverListener
    Type: callMBmainmethod
    Method: mainCheckBoxChecked()
    SubCheckbox
    clientListener
    Method: subClientCall
    Type: click
    serverListener
    Type: callMBsubmethod
    Method: subCheckBoxChecked()


  3. 在页面转入source模式,复制如下代码至<af: document ...<af: messages ...标签之间(参考2.5)
          <script type="text/javascript">
    <![CDATA[function mainClientCall (event)
    {
    var currcheckbox = event.getSource();
    var selectstatus = currcheckbox.getValue();

    //call method in manage bean
    AdfCustomEvent.queue(currcheckbox, "callMBmainmethod",{params:selectstatus}, true);
    }
    function subClientCall (event)
    {
    var currcheckbox = event.getSource();
    var selectstatus = currcheckbox.getValue();

    //call method in manage bean
    AdfCustomEvent.queue(currcheckbox, "callMBsubmethod",{params:selectstatus}, true);
    }
    ]]>
    </script>


  4. 修改页面对应Manage
    bean
    中的方法,修改mainCheckBoxChecked()subCheckBoxChecked()方法,添加如下代码
        public void getSelectedRows(ActionEvent actionEvent) {
    System.out.println("Selected Rows"); //debug
    System.out.println("******************************************"); //debug
    RowKeySet selectedRowsList = this.getMultipleCheckBoxProcess().getSelectedRowsList(this.getResId1());
    if (selectedRowsList != null && selectedRowsList.size() != 0) {
    for (Object temp : selectedRowsList)
    System.out.println("Get Row : " + temp);
    }
    System.out.println("******************************************"); //debug
    }
    public void CleanUpStatus(ActionEvent actionEvent) {
    this.getMultipleCheckBoxProcess().CleanUpStatus(this.getResId1(), "maincheckbox", "subcheckbox");
    }
    public void mainCheckBoxChecked(ClientEvent clientEvent) {
    Boolean currstatus = (Boolean)clientEvent.getParameters().get("params");
    this.getMultipleCheckBoxProcess().mainCheckBoxChecked(this.getResId1(), "subcheckbox", currstatus);
    }
    public void subCheckBoxChecked(ClientEvent clientEvent) {
    Boolean currstatus = (Boolean)clientEvent.getParameters().get("params");
    this.getMultipleCheckBoxProcess().subCheckBoxChecked(this.getResId1(), currstatus);
    }
    public MultipleCheckBox getMultipleCheckBoxProcess(){
    FacesContext ctx = FacesContext.getCurrentInstance();
    Application app = ctx.getApplication();

    MultipleCheckBox curr = (MultipleCheckBox)app.evaluateExpressionGet(ctx, "#{ viewScope.view_MultipleCheckbox}", Object.class);
    return curr;
    }


  5. 复制MultipleCheckbox.java至目标位置(在Demo中可以找到),在当前所使用的TaskFlow中添加Manage Bean名为view_MultipleCheckbox,Scope为view级别的Bean,指向MultipleCheckbox.java,修改MultipleCheckbox.java中的Package,使其与实际路径相符。


  6. 注意在适当的时候调用MultipleCheckbox的CleanUpStatus方法,一般在RichTable中数据发生改变时需要调用,如页面存在Query,则需在Query中调用MultipleCheckbox的CleanUpStatus方法。



补充:MultipleCheckBox中方法说明
  • public RowKeySet
    getSelectedRowsList(RichTable tartab);
    输入参数1:使用多选择CheckboxRichTable对象
    返回参数:选中的行的RowKey

  • public void setSelectedRowsList(RowKeySet
    selectedRowsList, RichTable tartab);
    输入参数1:选中行的RowKey
    输入参数2:使用多选择CheckboxRichTable对象

  • public void subCheckBoxChecked(RichTable
    tartab, Boolean checkStatus);
    输入参数1:使用多选择CheckboxRichTable对象
    输入参数2:当前Checkbox的状态
    该方法应在多Checkbox RichTable中某行前的Checkbox被选中/反选时调用

  • public void mainCheckBoxChecked(RichTable
    tartab, String subcheckboxid, Boolean newstatus);
    输入参数1:使用多选择CheckboxRichTable对象
    输入参数2:每行前Checkboxid
    输入参数3:当前全选Checkbox的状态
    该方法应在全选Checkbox被选中/反选时调用

  • public void CleanUpStatus(RichTable
    tartab, String maincheckboxid, String subcheckboxid);
    输入参数1:使用多选择CheckboxRichTable对象
    输入参数2:全选Checkboxid
    输入参数3:每行前Checkboxid
    该方法用于清除Checkbox的被选状态,通常需要执行查询前均需调用

Demo:
MultipleRowSelect1.rar

MultipleRowSelect2.rar

MultipleRowSelect3.rar

2009年4月16日星期四

在Region之间传递Event


概述

本文主要介绍如何在ADF应用中实现不同Region之间的event的传递。

下面以RegionA和RegionB为例,详细说明如何实现在Region之间传递event。

需求:在RegionA中点击导航栏中的任意一按钮时,在RegionB中触发相同事件。


实现

1、新建ADF应用,并根据需要创建基于Employees和Departments两个表的BC(EO/VO/AM)。

2、在ViewController项目中创建两个Bounded-Task-Flow with Fragments,这里名字分别为RegionA-task-flow和RegionB-task-flow。

3、从“Component Pallete”中分别拖“View”到刚创建成功的两个Task-Flow中,名字分别为RegionA和RegionB,双击根据向导生成两个.jsff的fragments,生成后的名字应分别为RegionA.jsff和RegionB.jsff。



4、打开RegionA.jsff,从“Data Controls”中拖动EmployeesView1到该fragment,根据提示生成form,并带有导航按钮。

5、同样的,打开RegionB.jsff,从“Data Controls”中拖动DepartmentsView1到该fragment,根据提示生成带有导航按钮的form。

6、新建.jspx页面,这里命名为main.jspx,使用“Panel Splitter”Layout进行布局,将页面分割成为左右两部分。分别将RegionA-task-flow和RegionB-task-flow拖到两部分中,生成后的页面如图所示: 


7、双击打开RegionA.jsff的PageDef文件,为其导航按钮添加event,查看“Structure”面板,以First按钮为例:在First按钮上点击右键,在“Insert inside First”中选择events,继续在events中点击右键,在“insert inside events”中选择event,命名为FirstEvent,为其他按钮添加同样的操作。进而对RegionB.jsff的PageDef中的导航按钮也添加同样的操作,完成后保存。


8、打开main.jspx页面,在“Structure”面板中的PageDef上点击右键,选择“Edit Event Map”,如图设置event的mapping关系,将所有的event都进行设置。


9、保存以上设置,运行main.jspx页面。
最终运行结果:


详细代码请查看:howtopasseventbetweenregions.rar

2009年4月15日星期三

监听dialog的取消按钮事件

function openPopup(evt){

var dialog = AdfPage.PAGE.findComponent('dlg2');

dialog.HandleEvent = function(event) {

if (event.getType() == AdfDialogEvent.EVENT_TYPE) {

event.propagatesToServer = function(){

return true;

}

}

}

AdfUIDialog.superclass.HandleEvent.call(this, event);

}

Manage Bean中向客户端写js

private void addScript(String script) {

FacesContext fc = FacesContext.getCurrentInstance();

ExtendedRenderKitService erks =

Service.getRenderKitService(fc, ExtendedRenderKitService.class);

erks.addScript(fc, script);

}

2009年3月30日星期一

在Task-Flow中实现页面间传值

概述

本文主要说明ADF中如何实现在Task-Flow中的页面之间传值。

在本示例中将创建两个页面,分别为PageAPageB,并完成PageAPageB中的传值。


实现

思路:

步骤一:使用set action listenerset property listener,设置fromtotypeset property listener,将当前页面的值设置到pageFlowScope中,例如设置在#{pageFlowScope.var1}

当然,在task-flow中传值时,可以在actionlistener里面设置值到pageFlowScope中。

步骤二:在页面中取值时,如果需要在一进入页面时就将该值取到并显示在页面中,可以将取值的相关代码写在页面对应的Backing Scope ManagedBean的构造方法中。

1、 创建应用,并基于EmployeesRegions两张表创建EO/VO,并将各自实例添加到AM中。

2、 创建Bounded Task-Flow,拖两个View到该Task-FlowId分别为PageAPageB,完成之间的导航设置。分别双击两个页面,用向导创建出PageA.jspxPageB.jspx

3、 PageA中,右键点击导航到PageB的按钮,在其中添加Set Action Listener,如图完成相关值的传递。

注:此时#{pageFlowScope.var1}会被标红,忽略此处。

4、打开PageB对应的Backing Scope ManagedBean,在其构造方法中添加如下代码:

RequestContext rc = RequestContext.getCurrentInstance();

String value = (String)rc.getPageFlowScope().get("var");

List list = ADFUtils.getBindingContainer().getAttributeBindings();

for (int i = 0; i <>

AttributeBinding abb = (AttributeBinding)list.get(i);

if (abb.getName().equals("FirstName")) {

abb.setInputValue(value);

}

}

rc.getPageFlowScope().clear();

5、保存以上更改和设置,运行该Task-Flow,查看结果:



相关代码请查看:howtopassvaluebetweenpages.rar

2009年3月14日星期六

使用LauchPopupListener过滤lov数据

概述

在实际应用中,lov(list of value)显示的数据往往会根据页面上的某些值进行过滤,此时,可以应用lauchpopuplistener来实现该功能。

实现

1、基于Employees表和Jobs表创建EO/VO/AM并设置Employees对应的VO中的JobId使用Jobs对应的VO作为数据源。

2、在Jobs表对应的VO上添加绑定变量,并修改SQL语句,将该绑定变量应用到SQL语句中,具体代码如下:

SELECT Jobs.JOB_ID,

Jobs.JOB_TITLE,

Jobs.MIN_SALARY,

Jobs.MAX_SALARY

FROM JOBS Jobs

WHERE Jobs.MIN_SALARY>:ls

3、为Employees对应的VO创建页面,并同时生成backingbean,设置JobId属性中的LaunchPopupListener为刚创建成功的backingbean中的方法,这里为filterlov()

4、在backingbean中的filterlov()方法中添加代码,完成数据过滤,具体代码如下:

public void filterlov(LaunchPopupEvent launchPopupEvent) {

BindingContext bctx = BindingContext.getCurrent();

BindingContainer bindings = bctx.getCurrentBindingsEntry();

FacesCtrlLOVBinding lov = (FacesCtrlLOVBinding)bindings.get("JobId");

lov.getListIterBinding().getViewObject().setNamedWhereClauseParam("ls", 400

}

5、保存以上设置,运行页面进行测试。

最终运行结果,完成了对于MIN_SALARY大于4000JobId的过滤:


相关代码:howtofilterlov

2009年2月27日星期五

简单实现多表插入

概述

本文主要描述在ADF开发中如何利用简单的方式实现多表插入。

场景:

在应用开发过程中,经常会遇到往一张或者两张表中插入/更新数据时,需要同时往另外的表中插入/更新数据,一般在遇到这样的情况时,需要基于所有需要实现插入/更新的表的EO创建VO,不过,如果是在VO已经创建完成,且待插入/更新的表的字段较少并与已存在VO关联的EO中的数据关系密切时,可以采用更简便的方式实现。

实现

1、创建名为Student的表,包含以下字段:S_IDS_NAMES_SEXDELETEFLAG

2、创建应用,基于EmployeesStudent表创建EO/VO并添加VO的实例到AM中。

3、分别生成EmployeesStudent两个EO的实现类。

4、在EmployeesImplEmployees EO的实现类)中改写doDML()方法为如下代码:

protected void doDML(int operation, TransactionEvent e) {

if (operation == DML_INSERT) {

EntityDefImpl student = StudentImpl.getDefinitionObject();

StudentImpl newStudent =

(StudentImpl)student.createInstance2(this.getDBTransaction(), null);

newStudent.setSId(this.getEmployeeId());

newStudent.setSName(this.getFirstName() + " " + this.getLastName());

}

super.doDML(operation, e);

}

5、保存应用,运行业务组件浏览器进行测试。

最终运行结果:


相关代码请查看:

howtocomplexinsert.rar