2009年9月20日星期日

ADF_034:ADF高级开发 之一:客制化与个性化 ADF 应用

运行环境:JDeveloper 11.1.2.1.0 + Oracle Database 10g Express Edition 10.2.0.1。

完成《Customizing and Personalizing an Application
这篇教程详细地说明了如何定制化ADF应用,由于文章较长,实验步骤较多,为了方便大家理解,我这里先介绍一下客制化与个性化的概念。
注意,我在这里把Customize翻译为“客制化”,把Personalize翻译为“个性化”,而把二者的统称为“定制化”。
所以在下文提到这些词的时候,希望大家能够明白我指的是那个英文单词。

我们在日常应用中经常会有这样的需求:
(1)每个用户希望能够保存自己常用的查询条件。
(2)每个用户对表格的显示方式风格不同(哪些字段显示或不显示,字段长度,前后顺序,是否冻结前几列)。
(3)虽然应用的功能类似,但不同的行业(industry)有不同的风格,比如金融行业,制造企业等等。
(4)接(3),虽然是同一个行业(industry),但仍然希望有不同的风格,比如虽然都是银行(finance),工商银行和中国银行的网站风格就不同。
ADF考虑到了这些方面的需求,并且允许用户做这样的定制化。ADF应用的定制化分为两种:
(1)个性化:允许用户运行时对应用进行定制,对应需求中的(1)和(2)。
(2)客制化:允许用户设计时对应用进行定制,对应需求中的(3)和(4)。
客制化的原理如下图所示:

从上图可以看出:
(1)一个ADF应用允许设置多个客制化层,如industry层及site层。
(2)每一个客制化层允许具有多个客制化值,如industry层可以具有healthcare和financial等。
(3)运行时,每一个层只有一个客制化值有效。
(4)客制化层的顺序由adf-config.xml中各个客制化类的顺序决定。
客制化的原理是:在设计时,在原有ADF应用的基础上,增加客制化层(可以有多层),每一层可能对应不同的行业、不同的公司。
这样做的好处是:既能满足不同风格的展现要求,同时又不改变原有应用的基础代码。

无论是客制化还是个性化,这些定制的内容均不会对已开发完成的应用作出修改,而是存储在MDS(Metadata Services repository)中。
MDS 支持两种存储方式的实现:文件和数据库。
ADF应用默认使用基于文件的MDS,关于如何使用基于数据库方式的MDS,会另文介绍。

好,下面就开始做这个实验。
本实验需要使用FOD Schema,请参考《发布与运行 Oracle Fusion Order Demo》。

重要步骤说明:

1. 修改CustomizationLayerValues.xml文件内容
配置global-level的客制化层要修改位于[jdev_home]/jdev/jdeveloper/jdev目录下的CustomizationLayerValues.xml文件。
配置application-level的客制化层,要点击adf-config.xml中的MDS Tab中的“Configure Design Time Customization Layer Values”。
这样会在应用的\.mds\dt\customizationLayerValues\目录中创建CustomizationLayerValues.xml文件。



<cust-layer name="site" id-prefix="s">
<cust-layer-value value="headquarters" display-name="Headquarters" id-prefix="1"/>
<cust-layer-value value="remoteoffices" display-name="Remote Offices" id-prefix="2"/>
<cust-layer-value value="site" display-name="Site"/>
</cust-layer>

在实验中只定义了一个客制化层:site,该客制化层可以设定为三个值:remoteoffice、headquarters、site。
你也可以定义多个客制化层。每个客制化层的先后顺序是定义在adf-config.xml文件中的。

2. 创建customization.properties文件
说明:customization.properties决定运行时每个客制化层使用哪个值。
在Model Project的src目录下创建customization.properties文件,内容如下:
#Configured values for the default layer values
#site=remoteoffices
site=headquarters
因为实验中只定义了一个客制化层:site,所以这里只给site客制化层赋值。
如果有多个客制化层,可以分别为每个层赋值,比如:
#Configured values for the default layer values
#industry=financial
#industry=healthcare
#site=remoteoffices
site=headquarters

3. 创建客制化层类:SiteCC
说明:一个客制化层对应一个客制化层类,客制化层类在运行时读取customization.properties文件,决定使用该客制化层使用哪个值。
其中要实现的接口方法有:
(1)CacheHint getCacheHint();
决定customization的类型,返回值包括ALL_USERS, MULTI_USER, REQUEST, USER四种。
ALL_USERS:customization为针对某应用全局有效的,通常用于static类型的customization层。
MULTI_USER:针对复数用户有效的customization。
REQUEST:针对当前请求有效的customization。
USER:针对某特定用户有效的customization,通过用户访问应用的Session来决定具体用户。
(2)String getName();
返回当前客制化层类对应的客制化层的值。
(3)String generateIDPrefix(RestrictedSession sess, MetadataObject mo);
返回在MDS中对当前客制化层元素加的前缀,以使该客制化层的元素在MDS中具有唯一标示。这一前缀在所有客制化层中必须是唯一的,出于性能考虑应小于4个字符。
(4)String[] getValue(RestrictedSession sess, MetadataObject mo);
返回指定客制化层的值,注意返回类型是个数组,其实只有一个值。

package oracle.model.mycompany;

import java.io.IOException;
import java.io.InputStream;

import java.util.Properties;

import oracle.mds.core.MetadataObject;
import oracle.mds.core.RestrictedSession;
import oracle.mds.cust.CacheHint;
import oracle.mds.cust.CustomizationClass;

public class SiteCC extends CustomizationClass {
    private static final String DEFAULT_LAYER_NAME = "site";
    private String mLayerName = DEFAULT_LAYER_NAME;

    public SiteCC() {
    }

    public SiteCC(String layerName) {
        mLayerName = layerName;
    }

    public CacheHint getCacheHint() {
        return CacheHint.ALL_USERS;
    }

    public String getName() {
        return mLayerName;
    }

    public String[] getValue(RestrictedSession sess, MetadataObject mo) {
        // This needs to return the site value at runtime.
        // For now, it's always null.
        Properties properties = new Properties();
        String configuredValue = null;
        Class clazz = SiteCC.class;
        InputStream is = clazz.getResourceAsStream("/customization.properties");
        if (is != null) {
            try {
                properties.load(is);
                String propValue = properties.getProperty(mLayerName);
                if (propValue != null) {
                    configuredValue = propValue;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new String[] { configuredValue };
    }
}

注意:建议修改getValue方法,不要每次都从customization.properties文件中读取值,这样会影响性能,应该放到建立一个缓存,放到缓存中。
把SiteCC类配置到adf-config.xml中。


4. 设置ViewController项目,允许客制化
右键ViewController项目,选择属性,选择ADF View,勾上“Enable Seeded Customizations”。
这样,ViewController项目就可以定制化了。


5. 创建基础的ADF Web应用,TaskFlows,JSF Pages,等等。
这一步就是开发一个普通的ADF Web应用,因此略去步骤。

6. 把JDeveloper切换到Customization Developer角色,准备客制化ADF应用
你可以客制化的内容不仅仅只有视图层,包括控制器层和模型层都可以客制化。
不能客制化的文件对象上面会有个“锁”的图标。
客制化分为使静态的(比如本实验)和动态的,所谓静态的就是事先知道用户使用的是哪些客制化层,每个客制化层的值是什么。
所谓动态就是事先不知道用户使用的是哪些客制化层以及每个客制化层的值,可能会根据登录用户的不同决定使用哪些客制化层。关于动态的客制化层,以后另文再述。
如果一切正常,换到Customization Developer角色后,你应该看到你设置的客制化层,如下图:

在本实验中,进行了以下客制化:
(1)site=headquarters时,在Browse.jsf页面中增加了一个Table,绑定到OrderItem。
(2)site=remoteoffices时,修改了OrderItemsView VO的定义,增加了来自ProductsBase EO中的ProductName字段。
(3)site=remoteoffices时,在Browse.jsf页面中增加了一个Table,绑定到OrderItem(此时已经多了ProductName字段)。
(4)site=remoteoffices时,在More.jsf页面中为Form外面罩了一个“Show Detail Header”布局组件
注意,以上的所有改动,都没有改变基础应用的原始代码,而是生成了对应的客制化的XML文件,见下两图:



7. 运行客制化后应用,看看不同的客制化效果
JDeveloper依然在Customization Developer角色下,分别前后两次修改customization.properties文件。
第1次改为site=headquarters,运行效果如下:

第2次改为site=remoteoffices,运行效果如下:



8. JDeveloper切换回Default Role,准备允许用户个性化
默认情况下,用户访问Web应用时所做的个性化设置是无法保存的,只能在每一次的请求中保留,发起下一次请求后,之前个性化设置就“丢掉了”。
ADF个性化设置有两种:
(1)For Duration of Session:用户个性化设置保存在用户的Session中,只要用户不退出应用,将一直有效。
(2)Across Session using MDS:用户个性化设置持久化在MDS中,即使用户退出应用也一直保留,用户再次登录后,依然可以使用自己之前保存的设置。甚至应用停掉后再重新启动,用户个性化设置依然保留。

8.1 用户个性化设置保存在用户的Session中
ADF Faces组件默认支持一些组件属性的个性化信息保存在用户的Session中。
因此只要设置ViewController项目属性如下就可以把用户个性化设置保存在用户的Session中了:

运行应用,做一些个性化设置,发现用户设置一直有效。重新开启一个IE或停掉应用后再重启,发现个性化设置“丢掉”了。

8.2 用户个性化设置持久化在MDS中
首先设置ViewController项目属性如下:

接着在adf-config.xml中增加一个UserCC个性化类(该类是ADF提供的)。

然后在adf-config.xml中告诉MDS,你要对哪些ADF Faces组件的哪些属性进行持久化。


9. Enable ADF Security,创建角色和测试用户并给角色授权
关于ADF Security,将在下一篇文章中专述,这里不解释。


10. 运行ADF应用,用不同的用户测试个性化
运行ADF应用,使用user1登录,做一些个性化设置,然后停掉应用再启动,再用user1登录,发现设置依然在。
那么用户的个性化设置是持久化在哪里了呢?
右键选择ADF应用,查看属性Run-MDS:

可以看出,用户个性化设置默认是持久化在文件系统当中的,比如:
C:\Oracle\JDevRuntime\system11.1.2.1.38.60.81\o.mds.ide.deploy.base\adrs\CustomizeApp\AutoGeneratedMar\mds_adrs_writedir\mdssys\cust\user\user1。
其中参数”preserve customizations across application run“是保留个性化设置在应用每次重新Run时。
”Delete customizations before each run"是在在应用每次重新Run时都清除个性化设置。
当然以上设置仅仅适合于开发环境,在生产环境中,应该持久化在数据库中。
在生产环境中使用EM Console发布应用时,会有一个MDS选项让你选择,就是为持久化用户个性化设置而用的。

11. 建议把customization.properties文件和SiteCC类单独打成一个Jar包
因为它们本身与应用逻辑无关,只是在运行时决定使用哪些客制化层,因此不要放在原始的应用代码中。
打包步骤如下:
(1)编译Model Project,发布Model Project为一个Jar文件

(2)选中 Include Manifest File

(3)只选中客制化类SiteCC和customization.properties文件。

(4)如果别的应用要使用该Jar,可以把该Jar文件复制到[jdev_home]\jdeveloper\jdev\lib\patches目录下,或者加到项目的Class Path和Library中。

Project 下载:CustomizeApp.7z

参考文献:
1. http://geekerdever.wordpress.com/2009/11/19/adf-customization-part-1-the-mds/
2. http://geekerdever.wordpress.com/2009/12/01/adf-customization-part-2-personalization/
3. http://geekerdever.wordpress.com/2009/12/07/adf-customization-part-3-customization/
4. http://geekerdever.wordpress.com/2010/01/25/customization-part-4-deploy/
5. http://biemond.blogspot.com/2009/07/customize-and-personalize-your-jsf.html
6. http://andrejusb.blogspot.com/2010/08/applying-personalization-and.html

没有评论: