You are browsing the archive for 2008 12月.

使用clientlistener和serverlistener刷新InlineFrame

12:18 pm in Oracle 融合中间件 by aronezhang

 

Oracle ADF 11g

 

应用背景

动态创建菜单结构

点击菜单项,InlineFrame组件中动态显示菜单项对应的页面

 

 

为了说明如何灵活应用ClientListenerServerListener,在本例子中将描述

  1. 静态菜单项和静态设置ClientListener和ServerListener结合应用
  2. 静态菜单项和动态设置ClientListener和ServerListener结合应用
  3. 动态菜单项和动态设置ClientListener和ServerListener结合应用

 

 

一、 静态菜单项和静态设置ClientListener和ServerListener

 

1,创建JSF页面,使用JDeveloper页面向导创建如下的页面

主页面RefreshInlineFrame.jspx,页面主要包括菜单条RichMenuBar和显示页面的InlineFrame组件

展现静态菜单项和静态设置ClientListener和ServerListener功能的StaticListenerMenuItem.jspx页面

展现静态菜单项和动态设置ClientListener和ServerListener功能的StaticMenuItemDynamicListener.jspx页面

展现动态菜单项和动态设置ClientListener和ServerListener功能的DynamicListenerMenuItem.jspx页面

 

2,添加客户端监听处理Javascript方法

为了捕捉用户点击菜单项的事件,并获得点击的是哪个菜单项,需要在页面中添加一个Javascript的方法来注册一个客户化事件,并将其添加到客户化事件队列中

?View Code JAVASCRIPT
1
2
3
4
5
function clientMethodCall(event) {                   
  adfRichMenuItem = event.getSource();
  AdfCustomEvent.queue(adfRichMenuItem, "customEvent",{clientId:adfRichMenuItem.getClientId()}, true);                                 
  event.cancel();
}

这个方法通过adfRichMenuItem.getClientId()获得点击的菜单项ID,并作为参数clientId返回给服务器

 

3,编写并注册Managed Bean:navigateBean

编写一个Managed Bean的实现类NavigateBean,其中包括一个服务器监听对应的方法来对客户端监听返回的信息进行处理,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void handleRequest(ClientEvent event) {
    String clientId = (String)event.getParameters().get("clientId");
    logger.info(clientId);
    if (clientId.equals("staticListenerMenuItem")) {
        setNavigateSourceURL("/faces/StaticListenerMenuItem.jspx");
    }
    if (clientId.equals("staticMenuItemDynamicListener")) {
        setNavigateSourceURL("/faces/StaticMenuItemDynamicListener.jspx");
    }
    if(clientId.equals("dynamicMenuItemAndListener")){
        setNavigateSourceURL("/faces/DynamicListenerMenuItem.jspx");
    }
}

方法中利用event.getParameters().get()来取得客户端监听方法中传过来参数的值,如上面就是通过clientMethodCall传过来的clientId这个参数的值,即菜单项的Id。

根据客户端传过来的参数值给InlineFrame组件设置source进而实现刷新

 

编写完成之后将其注册到faces-config.xml的配置文件中,并命名Managed Bean的名称为navigateBean

 

4,添加ClientListener和ServerListener监听

 

为了实现静态菜单项和静态设置ClientListener和ServerListener的功能,添加一个菜单项,并在菜单项下面添加

客户端和服务器监听,页面对应的XML代码如下:

1
 

客户端监听中指定执行的方法为上面创建好的Javascript方法clientMethodCall,类型为:action,代表当点击菜单项发出一个动作的时候触发

服务器监听中指定监听的类型,这个类型是客户端Javascript方法中添加事件队列是指定的,如上面的:customEvent;方法为Managed Bean中处理服务器监听的方法。

 

5,定义InlineFrame组件

往RefreshInlineFrame.jspx页面定义中添加<af:inlineFrame>组件来显示页面,代码如下:

1
 

partialTriggers:代表当触发哪些元素的时候刷新inlineFrame组件,刷新的内容来自于source参数的值。本文的例子中只要点击菜单项都需要刷新,

因此需要将菜单项的Id添加到这里,但是我们的一个菜单项是通过代码来创建的,因此这个参数的值需要动态

source:代表inlineFrame组件中显示的页面是什么,在我们的例子中,需要根据选择菜单项的不同变化为不同的页面地址和名称,因此这个参数的值也是需要动态传递的

 

为了实现动态传递参数的功能,我们需要利用Managed Bean的功能,添加2个私有变量,并定义它们的Accessor方法

1
2
private String navigateSourceURL;
private String[] nvaigatePartialTriggers;

 

 

二、静态菜单项和动态设置ClientListener和ServerListener

 

静态菜单项和动态设置ClientListener和ServerListener,只需要在RefreshInlineFrame.jspx页面定义中添加菜单项,不需要添加客户端和服务器监听组件。

它们通过Managed Bean动态设置,添加一个菜单项类型的私有变量,定义Accessor方法,并在其set方法中动态添加监听:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private RichCommandMenuItem richCommandMenuItem;
 
public void setRichCommandMenuItem(RichCommandMenuItem richCommandMenuItem) {
    this.richCommandMenuItem = richCommandMenuItem;
    if (this.richCommandMenuItem != null) {
        ClientListenerSet cls = new ClientListenerSet();
        cls.addListener("action", "clientMethodCall");
        cls.addCustomServerListener("customEvent",
                                    getClientEventMethodExpression("#{navigateBean.handleRequest}"));
        this.richCommandMenuItem.setClientListeners(cls);
    }
}
 
public MethodExpression getClientEventMethodExpression(String s) {
    FacesContext fc = FacesContext.getCurrentInstance();
    ELContext elctx = fc.getELContext();
    ExpressionFactory elFactory =
        fc.getApplication().getExpressionFactory();
    MethodExpression methodExpr =
        elFactory.createMethodExpression(elctx, s, null,
                                         new Class[] { ClientEvent.class });
    return methodExpr;
}

上面的代码的功能和我们在第一部分中步骤4添加监听组件是一样的,不同的只是这里通过代码动态添加。

 

方法完成之后需要将这个菜单项变量绑定到页面上:

1
 

 

 

三、动态菜单项和动态设置ClientListener和ServerListener

 

动态菜单项和动态设置ClientListener和ServerListener,要比上面两个情况稍微复杂一些

  1. 动态创建菜单
  2. 动态绑定监听

 

往Managed Bean的类中添加如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private RichMenu dynamicRichMenu;
 
public void createDynamicMenu(PhaseEvent phaseEvent) {
    RichCommandMenuItem richMenuItem = new RichCommandMenuItem();
    richMenuItem.setId("dynamicMenuItemAndListener");
    richMenuItem.setText("Dynamic Item and Listener");
    richMenuItem.setActionExpression(getActionEventMethodExpression("dynamic"));
 
    ClientListenerSet cls = new ClientListenerSet();
    cls.addListener("action", "clientMethodCall");
    cls.addCustomServerListener("customEvent",
                                getClientEventMethodExpression("#{navigateBean.handleRequest}"));
    richMenuItem.setClientListeners(cls);
    dynamicRichMenu.getChildren().add(richMenuItem);
}
 
private MethodExpression getActionEventMethodExpression(String name) {
    Class[] argtypes = new Class[1];
    argtypes[0] = ActionEvent.class;
    FacesContext facesCtx = FacesContext.getCurrentInstance();
    Application app = facesCtx.getApplication();
    ExpressionFactory elFactory = app.getExpressionFactory();
    ELContext elContext = facesCtx.getELContext();
    return elFactory.createMethodExpression(elContext, name, null,
                                            argtypes);
}

 

修改RefreshInlineFrame.jspx的页面定义:

在页面中添加beforePhase,调用创建菜单的方法<f:view beforePhase=”#{navigateBean.createDynamicMenu}”>

将菜单项的私有变量绑定到页面中<af:menu text=”Show Listener(Dynamic Memu)” id=”dynamicMenu”  binding=”#{navigateBean.dynamicRichMenu}”/>

 

 

四、运行RefreshInlineFrame.jspx页面

 

listener_inlineframe

 

选择不同的菜单项,下面显示不同的内容。

 

 

五、总结

 

本文中利用了ClientListener和ServerListener两个监听静态和动态的配合来实现动态刷新inlineFrame的内容,本文中利用了一些常用的技巧:

  1. 动态创建菜单
  2. 动态绑定一个Action
  3. 动态绑定监听
  4. 客户端和服务器监听的应用
  5. 如何利用Managed Bean来动态获得需要的值
  6. 应用组件的partailTriggers
  7. af:inlineFrame组件的应用

 

 

下载JDeveloper 11g 项目文件:Download Oracle EBS二次开发系列视频教程之反安装开发工具 Version 1

 

超强笔误:巴屎烤肉

7:38 am in 生活点滴/Enjoy Life by aronezhang

 

那天带着几位朋友自驾到上海郊区去玩,晚上回来出上海收费站都已经6点多了,由于大家都比较饥饿,因此商量着准备找一家自助烤肉店饱餐一顿。

大家最后讨论下来决定找一家巴西的烤肉店,由于不知道具体的路怎么走,LP建议还是利用一下高科技产品:GPS导航仪。

现在的GPS导航仪都是触摸屏的,需要找地址的时候都是手写录入的,准备手写录入的时候,LP提醒那个巴西烤肉的店名不叫“巴西烤肉”,

让我注意一下其中XI字的拼写,否则找不到烤肉店的地址。可是一想那个字不知道怎么拼写啊。

问:哪个XI啊?

答:XI牛的那个XI

问:那个字我还不会写,怎么拼呢?(都是电脑害的,现在难得写几个字,真是不容易啊)

答:好像是上面一个尸体的尸体,下面一个米字

 

我就按照LP的提示在导航仪的触摸屏上面将那个“XI”自写了出来,一看屏幕的字,一身爆汗!!!

shi_word

 

啊!!?!!,这不是(SHI)吗?哪里是XI字啊(还好字不会写但是还认得,哈哈!!!)

 

然后一车的人都笑翻了,从此多了一个笑话:巴屎烤肉

 

OAF中多语言转换开发实现解析

6:37 am in Oracle EBS二次开发 by aronezhang

 

Oracle EBS 12.0.0

 

OAF中多语言转换开发实现步骤 一文介绍了OAF多语言转换开发的步骤。

但也产生了如下的疑问:

  1. 程序怎么知道需要不需要进行多语言转换?
  2. 页面中也没有TL表相关的视图对象,它的多语言数据又怎么进去?
  3. 在OAF开发中,需要往数据库插入数据,都是通过EO的操作来实现,但是按照开发手册,我们是基于VL视图来创建EO,复合式视图又无法插入数据,这是怎么回事?
  4. 而多语言转换的基表根本就没有定义EO,它是什么时候插入数据的?

 

带着上面的疑问,我们来看看实体对象的实现类OAEntityImpl是如何来实现多语言转换功能的

 

一、首先来看看EO的create方法

1
2
3
4
5
6
7
8
public void create(AttributeList paramAttributeList)
  {
    super.create(paramAttributeList);
    createRowWho();
    createObjectVersionNumber();
    if (isTranslatable())
      createTranslated(paramAttributeList);
  }

 

其中isTranslatable()来判读是否需要多语言的转化,如果需要就调用createTranslated()方法,否则就按照默认的创建方式调用父类的create方法

isTrnaslattable()方法最终是通过getBaseTableName()方法来判断是否需要实现多语言转换:

 

1
2
3
4
private String getBaseTableName()
{
  return ((String)getEntityDef().getProperty(&quot;OA_BASE_TABLE&quot;));
}

 

getBaseTableName()方法取得实体对象的自定义实体属性OA_BASE_TABLE,这个属性就是开发时定义在VL视图的实体对象上面的,所以只要设置了这个属性,就会被认为需要进行多语言的转换

 

上面回答了我的第一个疑问:程序如何知道多语言转换

 

二、看看专门为多语言转换所设计的createTranslated()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private RowIterator createTranslated(AttributeList paramAttributeList)
{
  RowIterator localRowIterator = getTLEOIterator();
  String str = getOADBTransaction().getCurrentLanguage();
  String[] arrayOfString2 = getInstalledLanguages();
  int j = getTLLanguageAttributeIndex();
  int k = getTLSourceLangAttributeIndex();
  for (int l = 0; l &lt; arrayOfString2.length; ++l)
  {
    RowImpl localRowImpl = (RowImpl)localRowIterator.createRow();
    localRowImpl.setAttribute(j, arrayOfString2[l]);
    localRowImpl.setAttribute(k, str);
    localRowIterator.insertRow(localRowImpl);
  }
  return localRowIterator;
}

 

getTLEOIterator()方法则是返回TL对应的EO行集实例,看到代码中的一个for循环,循环是基于目前已安装的语言进行的,因此当往VL视图实体对象中插入数据的时候,就会按照已安装的语言往TL表中插入记录

这里回答了:如何插入TL表的数据,而且还了解了为什么一次可以将所有语言环境的信息都插入到TL表中。

 

另外关于如何找到VL实体对象对应的TL实体对象就留个读者自己去完成吧(OATranslatedEntityStaticInfo类中)

 

但是这里并没有看到数据往多语言基表插入的程序,让我们继续浏览代码。

 

三、查看生成DML语句的buildDMLStatement方法

 

1
2
3
4
5
6
7
8
9
10
11
protected StringBuffer buildDMLStatement(int paramInt, AttributeDefImpl[] paramArrayOfAttributeDefImpl1, AttributeDefImpl[] paramArrayOfAttributeDefImpl2, AttributeDefImpl[] paramArrayOfAttributeDefImpl3, boolean paramBoolean)
{
  if (!(isTranslatable()))
    return super.buildDMLStatement(paramInt, paramArrayOfAttributeDefImpl1, paramArrayOfAttributeDefImpl2, paramArrayOfAttributeDefImpl3, paramBoolean);
  StringBuffer localStringBuffer = super.buildDMLStatement(paramInt, trimXlatAttributes(paramArrayOfAttributeDefImpl1), trimXlatAttributes(paramArrayOfAttributeDefImpl2), trimXlatAttributes(paramArrayOfAttributeDefImpl3), paramBoolean);
  if (localStringBuffer == null)
    return null;
  String str = localStringBuffer.toString();
  str = OACommonUtils.replace(str, getEntityDef().getSource(), getBaseTableName());
  return new StringBuffer(str);
}

 

其中最关键的一句是OACommonUtils.replace(str, getEntityDef().getSource(), getBaseTableName()); ,这个语句将本来是VL视图的名字getEntityDef().getSource()替换为多语言基表的名字getBaseTableName(),

这样看似是往VL实体对象中插入数据,而实际上已经被偷梁换柱了。它根本就不会往VL视图对象上面进行DML操作。

这样一来我们首先提出的3,4疑问都已经得到回答:为什么可以操作视图和TL的多语言基表的数据是怎么被插入的?

 

但是细心的人会发现新的问题了。上面的buildDMLStatement方法中,难道仅仅将DML语句中的表对象更换了就可以了吗?

答案是否定的,因为VL的数据库列和多语言基表是不一样的,基于VL实体对象中的列属性比基表多了多语言的那些字段。

 

四、查看多语言属性剔除方法trimXlatAttributes

 

从代码中注意到buildDMLStatement方法调用了trimXlatAttributes()方法来处理了传入的参数,让我们看看trimXlatAttributes()方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private AttributeDefImpl[] trimXlatAttributes(AttributeDefImpl[] paramArrayOfAttributeDefImpl)
{
  if (paramArrayOfAttributeDefImpl == null)
    return null;
  int i = 0;
  for (int j = 0; j &lt; paramArrayOfAttributeDefImpl.length; ++j)
    if (!(isTranslatableAttribute(j)))
      ++i;
  AttributeDefImpl[] arrayOfAttributeDefImpl = new AttributeDefImpl[i];
  int k = 0;
  for (int l = 0; l &lt; paramArrayOfAttributeDefImpl.length; ++l)
    if (!(isTranslatableAttribute(l)))
    {
      arrayOfAttributeDefImpl[k] = paramArrayOfAttributeDefImpl[l];
      ++k;
    }
  return arrayOfAttributeDefImpl;
}

 

代码中有isTranslatableAttribute()方法来判断相应的列是否为多语言转换的列,如果不是则添加到arrayOfAttributeDefImpl[]数组,否则丢弃。

这样处理之后返回的数据就只包括了除去多语言属性的列,这刚好是多语言基表中的属性列,所以上面buildDMLStatement()方法中将数据表替换为多语言基表才不会出现问题。

 

至此多语言实现处理的主要逻辑都已经解释完成了,另外有很多细节上面的处理就留个感兴趣的人去研究吧。

 

 

附上OAEntityImpl.class,OATranslatedEntityStaticInfo.class反编译的Java代码(仅供学习之用) : Download 再议OAF中的记录历史功能 Version 1

 

 

OAF中多语言转换开发实现步骤

2:45 am in Oracle EBS二次开发 by aronezhang

 

Oracle EBS 12.0.0

 

Oracle EBS 12.0.0 开始,OA Framework的 多语言转换的实现基本上已经和一般的实现没有什么区别,大大简化了开发的复杂性。

下面是基本的开发步骤:

 

1,按照下面的规则为 _TL表创建一个实体对象

 

命名实体为<Entity>TLEO,如LOOKUP_CODES_TL表被命名为LookupCodeTLEO实体名称

  1. 包括表中所有的列,并且保证LANGUAGE列被命名为Language属性,SOURCE_LANG列被命名为SourceLang属性
  2. 然后设置LANGUAGE列作为主键的一部分
  3. 并且保证实体和其它实体一样继承了OAEntityImpl
  4. 其它的实体相关的验证逻辑,多语言转换的无需添加任何验证

 

默认情况下,只要满足如下条件的所有字段都会被认为是需要进行多语言转换的:

  1. 不是主键列,而且
  2. 不是一个entity accessor,而且
  3. 是数据类型之一:VARCHAR, CHAR, FIXED_CHAR, LONGVARCHAR, CLOB

 

如果说需要覆盖默认的规则,可以在各属性级别的客户化属性中设置OA_TRANSLATABLE属性,

属性值为true,说明此列需要进行多语言转换

属性值为false,说明此列不需要进行多语言转换

 

2,按照下面的规则为 _VL视图创建一个多语言实体对象

 

  1. 按照创建一般实体对象的规则来创建实体对象,如LOOKUP_CODES_VL视图,对应的实体名称为LookupCodesEO
  2. 不要包括伪列RowId,包括视图中其它所有的列
  3. 检查主键正确设置
  4. 设置一个实体级别的自定义属性OA_BASE_TABLE,它的值为多语言视图_VL所对应的基表,如LOOKUP_CODES_B或者LOOKUP_CODES

 

OA Framework自动覆盖了实体对象中的doDML方法来确保当对多语言视图进行插入、更新和删除操作的时候实际上是对基表进行相应的操作。所有的读取操作则依赖于_VL视图

 

3,根据以下规则在_VL(Source)和_TL(destination)间创建一个实体关联

 

  1. 按照常规的关联规则创建
  2. 关系必须是 1:*,取消Cascade Delete选项的选择,_VL实体选择为Source,_TL实体为Destination
  3. 更改destination entityAccessor Name名称为OA_TL_ENTITIES

 

4,使用多语言视图创建视图对象

 

当创建一个视图对象来访问多语言转换实体的时候,总是使用基于_VL视图创建的实体对象。基于EBS的设计,访问多语言都是基于VL,TL被自动处理为“不可见”状态

 

上面的开发步骤除了需要进行几个必要属性的设置之外,和非多语言转换的开发步骤基本上没有什么区别,而在EBS R12之前,如果需要进行多语言转换的开发,

需要使用OAPlsqlEntityImpl来实现,同时需要编写数据库程序包 ON_INSERT、ON_UPDATE、ON_DELETE 和 ON_LOCK 方法。而从EBS R12版本之后,已经进行了简化。

 

后面我们就来看看Oracle究竟是怎么样来解决这个问题,使开发人员无须更多关注多语言转换开发实现的技术细节。

 

本站参考链接

 

关于Oracle EBS下多语言的开发,请查看:

数据多语言转换开发(1)

http://www.oracleseeker.com/index.php/data-translation-in-ebs-forms-breifing

数据多语言转换开发(2)

http://www.oracleseeker.com/index.php/table-relation-of-data-translation-in-ebs-forms

数据多语言转换开发(3)

http://www.oracleseeker.com/index.php/develop-data-translation-form-in-oracle-ebs