2009年9月30日星期三

ADF_050:ADF 11g 问与答

注意,做实验时,发现保存个性化查询条件,会报错:oracle.mds.exception.ReadOnlyStoreException: MDS-01273: ...
此时,需要修改adf-config.xml,内容如下:
<mds-config> ......
<cust-config> ......
</cust-config>
<persistence-config>
<metadata-namespaces>
<namespace path="/persdef" metadata-store-usage="MAR_TargetRepos"/> </metadata-namespaces>
<metadata-store-usages>
<metadata-store-usage id="MAR_TargetRepos" deploy-target="true" default-cust-store="true">
</metadata-store-usage>
</metadata-store-usages>
</persistence-config> ......
</mds-config>

5. 如何在页面中嵌入flash,并且自动播放?
ADF中有一个media组件,可以支持播放视频和音频,但试验后发现,只有音频可以自动播放,视频无法自动播放,即使设置了autostart=" true " 也不行。
Oracle ADF开发小组承认这个功能有BUG,在下一个版本会FIX。
所以只好另辟蹊径,建议使用swfobject.js来实现。swfobject是一个第三方的javascript,非常好用,支持大多数浏览器。下载地址:http://code.google.com/p/swfobject/downloads/list
具体用法如下:
(1)在.html中
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</meta> <title>untitled1</title>
<script type="text/javascript" src="swfobject.js">
</script>
<script type="text/javascript">
var flashvars = false;
var params = { };
params.wmode = "Transparent";
swfobject.embedSWF("popupintable.swf", "flashcontent", "932", "364", "9.0.0", "expressInstall.swf", flashvars, params);
</script>
</head>
<body>
<div id="flashcontent">This text is replaced by the Flash movie.</div>
</body>
</html>
(2)在.jspx中
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:af="http://xmlns.oracle.com/adf/faces/rich%22&gt;
<jsp:directive.page contentType="text/html;charset=UTF-8"/>
<f:view>
<af:document id="d1">
<af:resource source="swfobject.js" type="javascript"/>
<af:resource type="javascript">
var flashvars = false;
var params = { };
params.wmode = "Transparent";
swfobject.embedSWF("../popupintable.swf", "flashcontent", "932", "364", "9.0.0", "../expressInstall.swf", flashvars, params);
</af:resource>
<af:form id="f1">
<f:verbatim>
<div id="flashcontent">This text is replaced by the Flash movie.</div>
</f:verbatim>
</af:form>
</af:document>
</f:view>
</jsp:root>
注意,由于JSF的访问路径包含faces,所以flash文件的相对路径要向上一个目录。

6. 发布ADF Web应用时,出现异常如下:
oracle.jbo.DMLException: JBO-26061: Error while opening JDBC connection.at oracle.jbo.server.ConnectionPool.createConnection(ConnectionPool.java:253)at oracle.jbo.server.ConnectionPool.instantiateResource(ConnectionPool.java:168)at oracle.jbo.pool.ResourcePool.createResource(ResourcePool.java:546)at oracle.jbo.pool.ResourcePool.useResource(ResourcePool.java:327)at oracle.jbo.server.ConnectionPool.getConnectionInternal(ConnectionPool.java:104)Truncated. see log file for complete stacktracejava.sql.SQLException: ORA-01005: null password given; logon denied

在setDomainEnv.cmd 脚本中增加一个JAVA_PROPERTIES:
-Djps.app.credential.overwrite.allowed=true。

7. 如何让一个普通Project可以使用JSF/ADF组件?
(1)右键点击Project,选择Project Properties,选择JSP Tag Libraries,选择Select Distributed libraries,增加ADF Faces Components 11,(只选这个就可以,其它的会带过来)。
(2)继续选择Technology Scope,选择JSF(只选这个就可以,其它的会带过来)。
如果你需要使用ADF Controller,你还要增加。

7. 图表中的饼状图,当小于某一个值的项目过多时,能否自动归类成一个项目:其它?

8. 用户的自定义查询条件能否保存,避免用户每次退出/登录都要重新设置?
可以,具体设置请参考《客制化与个性化 ADF 应用》。

9. 表格的自定义能否保存,避免用户每次退出/登录都要重新设置?
可以,具体设置请参考《客制化与个性化 ADF 应用》。

10. ValueBinding已经不建议使用了,那么如何实现以前的功能,比如:
ValueBinding vb = facesContext.getApplication().createValueBinding("#{bindings}");
DCBindingContainer dc = (DCBindingContainer) vb.getValue(facesContext);

可以。使用 resolveExpression方法就可以,该方法在JSFUtil.java中。

11. 如何实现点击一个按钮,实现一个异步操作。得到响应后,再以某种方式通知页面?
http://forums.oracle.com/forums/thread.jspa?threadID=999826

12. 使用WLS Console启动Manage Server时,无法启动ADF Liberary,抛出异常java.lang.ClassNotFoundException: oracle.jrf.wls.JRFStartup?
(1)修改[Oracle_HOME]\wlserver_10.3\common\nodemanager\nodemanager.properties。
设置 StartScriptEnabled=true。
(2)重启Node Manager。
(3)重启Manager Server。

13. 柱状图的Y轴的数据有时悬殊比较大,是否能够自适应,能否设置最大值,最小值?
可以设置最大值和最小值。

14. 如果表单内容是动态的(有时项目多,有时项目少),表单下面的按钮能否随着内容的多少始终与最后一个项目保持同样的距离?

15. 表单中的选项为Required时,错误提示可以放在其它地方吗(目前默认是在该选项的右边)?

16. 某个输入选项的值,必须通过查询得到,查询条件在另一个窗口,如何把查询结果返回给该输入选项?

17 使用柱状图时,横轴文字很长,是否会自动换行?

18. 表格的excel导出功能,能否带格式导出(比如虚线效果)?

19. 1. ADF 是否支持SDO,如何使用?

20. Office 软件如何与JSR 227交互?

21. 支持Struts的哪个版本?

22. 如何在.jspx中增加JavaScript脚本?
<f:facet name="metaContainer"> <af:resource type="javascript" source="/js/deletewithkey.js"/> </f:facet>
有关resource Tag的详细说明,请参考http://jdevadf.oracle.com/adf-richclient-demo/docs/tagdoc/af_resource.html

23. 使用IE8做实验时,所有的按钮都不起作用,浏览器左下角出现错误提示:
行: 5660
字符: 2
代码: 0
URI: http://127.0.0.1:7101/HRSystem-ViewController-context-root/afr/partition/ie/default/opt/boot-11.1.1.3.0-0084.js

解决方案:
(1)打开注册表编辑器:regedit。
(2)找到HKEY_CLASSES_ROOT\TypeLib\{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}\1.1\0\win32。
(3)找到"C:\WINDOWS\system32\shdocvw.dll",将shdocvm.dll修改为ieframe.dll。
(4)4.重启IE 。

1.Create and CreateInsert
2.Binding Objects in the Executables Section
3.Controlling Execution of Executables
4.Binding Objects in the Parameters Section,如何访问页面参数
5.JSF Lifecycle: Is repeated many times within a user application session
6. Any executables that have their refresh attribute set to prepareModel are refreshed7. Lifecycle listeners can be configured for the following scopes
8. ADF RC run stand alone?
9. Peer Object
10.Page-level security is not checked within bounded task flows
11.Shared memory scope enables data to be passed between activities within the task flow
12.Save Point and Restore
13.改变Java to EJB 界面是否要修改?Create Data Control 是否要重新生成?是否有变化?如果接口没变?
14.只用JSF/JSP,还能用拖拽绑定?

2009年9月29日星期二

ADF_049:发布与运行 Oracle Fusion Order Demo

FOD Demo是ADF非常重要的一个Demo,是学习ADF的进阶教材。每次推出新版本的JDeveloper和ADF时,都会更新这个Demo。
下载地址:http://www.oracle.com/technetwork/developer-tools/jdev/index-095536.html。
注意,必须使用和JDeveloper 相匹配的FOD Demo 版本(链接中有说明),否则可能会发布失败。
下面介绍一下如何发布与运行FOD Demo。

1. 发布
解压zip包后,使用JDeveloper打开Infrastucture Application。打开Infrastucture Project,build.properties 文件,修改以下内容,和你的环境相匹配。
这个是我的环境,对比修改成你的。
jdeveloper.home=C:/Oracle/Middleware/jdeveloper
src.home=..//..
# JDBC info used to create Schema
jdbc.driver=oracle.jdbc.OracleDriver
jdbc.urlBase=jdbc:oracle:thin:@localhost
jdbc.port=1521
jdbc.sid=XE
# Information about the default setup for the demo
user.db.adminUser=system
db.adminUser.password=welcome1
db.demoUser=FOD
db.demoUser.password=fusion
db.demoUser.tablespace=USERS
db.demoUser.tempTablespace=TEMP

在发布前,还要修改一个地方,这是我自己总结出来的。
在Infrastucture Project中找到PRODUCT_TRANSLATIONS.sql文件,用JDeveloper打开,修改第3条Insert语句,在这条语句的最后有一个小方块,去掉它。
不然的话,会导致发布错误。至于原因,我还没有搞清楚,大概和数据库字符集有关,设置成中文大概就可以了。

右键点击 build.xml,选择 Run Ant Target > buildAll ,如果一切顺利,你应该看到所有语句都执行成功,最后显示:
BUILD SUCCESSFUL
Total time: 32 seconds

2. 运行StoreFrontModule
使用JDeveloper打开StoreFrontModule Application。
在 Application Navigator中检查一下,数据库连接是否正确,如果发布完全成功的话,应该能够看到FOD Schema 的所有对象。
右键点击StoreFrontService Project,选择Rebuild。
右键点击StoreFrontUI Project,选择Run。


3. 运行MasterPriceList
使用JDeveloper打开MasterPriceList Application。
在 Application Navigator中检查一下,数据库连接是否正确,如果发布完全成功的话,应该能够看到FOD Schema 的所有对象。
右键点击Model Project,选择Rebuild。
右键点击ViewController Project,选择Rebuild。
右键点击login.jspx,选择Run。
登录账户有两个:
(1)sking/welcome1 Administrator角色 可以访问并修改信息。
(2)ahunold/welcome1 Manager角色 可以访问信息,但不可以修改信息。


4. Oracle Fusion Order Demo Application 介绍
(1)Infrastructure - 应用所需的基础信息都在这个应用中:创建用户、数据库表、数据。
(2)MasterPriceList - 与Excel集成,展示ADF 桌面应用集成功能。
(3)StandaloneExamples - 包含一些独立的项目,展示Store Front 中没有展示的功能。
(4)StoreFrontModule - 使用了大量ADF技术开发的Web应用:一个电子购物网站。该应用还提供了与CompositeServices module集成的接口。

2009年9月28日星期一

ADF_048:发布与运行ADF Faces Rich Client Components Demo

ADF Faces组件非常多,令人眼花缭乱,掌握起来要花一定的时间。
为此ADF Faces RC 开发团队特地开发一个ADF Faces Rich Client Components Demo。
这是一个在线演示的Demo,观看地址:http://jdevadf.oracle.com/adf-richclient-demo/faces/index.jspx。

你也可以把它下载下来,下载地址:http://jdevadf.oracle.com/adf-richclient-demo/docs/release.html,点击"download",下载rcf-dvt-demo.war。
在ADF的官网上,也提供了同样的下载:http://www.oracle.com/technetwork/developer-tools/adf/downloads/index.html。

这是个war文件,你可以把它安装到本地运行,这样你可以看到所有源代码。
注意,必须使用和JDeveloper 相匹配的Demo版本(链接中有说明),否则可能会发布失败。
下面介绍一下如何安装与运行该Demo。

1. 安装
(1)启动JDeveloper,选择新建Applications,输入一个应用名称,比如ADF_RC_Demo。
(2)删除掉新建Applications自带的Projects。选择新建Projects,选择 select Project from WAR File。

(3)输入一个Project名称,比如ADF_RC,选择rcf-dvt-demo.war,开始导入该war文件。
这一步时间会较长,因为war文件有200多M,你可以休息一下。
(4)选择Project Properties,选择Run Debug Profile,修改,选择Tool Settings,在Before Running中不选择Make Project选项。


2. 运行
右键点击index.jspx,选择Run。

ADF_047:ADF工具类:ADFUtil.java 源代码

ADFUtil.java包含了很多工具方法。

package com.javaneverdie.adfdemo.table.view.util;

import java.util.ArrayList;
import java.util.List;

import javax.faces.model.SelectItem;

import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCIteratorBinding;
import oracle.adf.model.binding.DCParameter;

import oracle.adf.share.logging.ADFLogger;

import oracle.binding.AttributeBinding;
import oracle.binding.BindingContainer;

import oracle.binding.ControlBinding;

import oracle.binding.OperationBinding;

import oracle.jbo.ApplicationModule;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.uicli.binding.JUCtrlValueBinding;


/**
* A series of convenience functions for dealing with ADF Bindings.
* Note: Updated for JDeveloper 11
*
* @author Duncan Mills
* @author Steve Muench
*
* $Id: ADFUtils.java 2513 2007-09-20 20:39:13Z ralsmith $.
*/
public class ADFUtils {

public static final ADFLogger LOGGER =
ADFLogger.createADFLogger(ADFUtils.class);

/**
* Get application module for an application module data control by name.
* @param name application module data control name
* @return ApplicationModule
*/
public static ApplicationModule getApplicationModuleForDataControl(String name) {
return (ApplicationModule)JSFUtils.resolveExpression("#{data." + name +
".dataProvider}");
}

/**
* A convenience method for getting the value of a bound attribute in the
* current page context programatically.
* @param attributeName of the bound value in the pageDef
* @return value of the attribute
*/
public static Object getBoundAttributeValue(String attributeName) {
return findControlBinding(attributeName).getInputValue();
}

/**
* A convenience method for setting the value of a bound attribute in the
* context of the current page.
* @param attributeName of the bound value in the pageDef
* @param value to set
*/
public static void setBoundAttributeValue(String attributeName,
Object value) {
findControlBinding(attributeName).setInputValue(value);
}

/**
* Returns the evaluated value of a pageDef parameter.
* @param pageDefName reference to the page definition file of the page with the parameter
* @param parameterName name of the pagedef parameter
* @return evaluated value of the parameter as a String
*/
public static Object getPageDefParameterValue(String pageDefName,
String parameterName) {
BindingContainer bindings = findBindingContainer(pageDefName);
DCParameter param =
((DCBindingContainer)bindings).findParameter(parameterName);
return param.getValue();
}

/**
* Convenience method to find a DCControlBinding as an AttributeBinding
* to get able to then call getInputValue() or setInputValue() on it.
* @param bindingContainer binding container
* @param attributeName name of the attribute binding.
* @return the control value binding with the name passed in.
*
*/
public static AttributeBinding findControlBinding(BindingContainer bindingContainer,
String attributeName) {
if (attributeName != null) {
if (bindingContainer != null) {
ControlBinding ctrlBinding =
bindingContainer.getControlBinding(attributeName);
if (ctrlBinding instanceof AttributeBinding) {
return (AttributeBinding)ctrlBinding;
}
}
}
return null;
}

/**
* Convenience method to find a DCControlBinding as a JUCtrlValueBinding
* to get able to then call getInputValue() or setInputValue() on it.
* @param attributeName name of the attribute binding.
* @return the control value binding with the name passed in.
*
*/
public static AttributeBinding findControlBinding(String attributeName) {
return findControlBinding(getBindingContainer(), attributeName);
}

/**
* Return the current page's binding container.
* @return the current page's binding container
*/
public static BindingContainer getBindingContainer() {
return (BindingContainer)JSFUtils.resolveExpression("#{bindings}");
}

/**
* Return the Binding Container as a DCBindingContainer.
* @return current binding container as a DCBindingContainer
*/
public static DCBindingContainer getDCBindingContainer() {
return (DCBindingContainer)getBindingContainer();
}

/**
* Get List of ADF Faces SelectItem for an iterator binding.
*
* Uses the value of the 'valueAttrName' attribute as the key for
* the SelectItem key.
*
* @param iteratorName ADF iterator binding name
* @param valueAttrName name of the value attribute to use
* @param displayAttrName name of the attribute from iterator rows to display
* @return ADF Faces SelectItem for an iterator binding
*/
public static List selectItemsForIterator(String iteratorName,
String valueAttrName,
String displayAttrName) {
return selectItemsForIterator(findIterator(iteratorName),
valueAttrName, displayAttrName);
}

/**
* Get List of ADF Faces SelectItem for an iterator binding with description.
*
* Uses the value of the 'valueAttrName' attribute as the key for
* the SelectItem key.
*
* @param iteratorName ADF iterator binding name
* @param valueAttrName name of the value attribute to use
* @param displayAttrName name of the attribute from iterator rows to display
* @param descriptionAttrName name of the attribute to use for description
* @return ADF Faces SelectItem for an iterator binding with description
*/
public static List selectItemsForIterator(String iteratorName,
String valueAttrName,
String displayAttrName,
String descriptionAttrName) {
return selectItemsForIterator(findIterator(iteratorName),
valueAttrName, displayAttrName,
descriptionAttrName);
}

/**
* Get List of attribute values for an iterator.
* @param iteratorName ADF iterator binding name
* @param valueAttrName value attribute to use
* @return List of attribute values for an iterator
*/
public static List attributeListForIterator(String iteratorName,
String valueAttrName) {
return attributeListForIterator(findIterator(iteratorName),
valueAttrName);
}

/**
* Get List of Key objects for rows in an iterator.
* @param iteratorName iterabot binding name
* @return List of Key objects for rows
*/
public static List keyListForIterator(String iteratorName) {
return keyListForIterator(findIterator(iteratorName));
}

/**
* Get List of Key objects for rows in an iterator.
* @param iter iterator binding
* @return List of Key objects for rows
*/
public static List keyListForIterator(DCIteratorBinding iter) {
List attributeList = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
attributeList.add(r.getKey());
}
return attributeList;
}

/**
* Get List of Key objects for rows in an iterator using key attribute.
* @param iteratorName iterator binding name
* @param keyAttrName name of key attribute to use
* @return List of Key objects for rows
*/
public static List keyAttrListForIterator(String iteratorName,
String keyAttrName) {
return keyAttrListForIterator(findIterator(iteratorName), keyAttrName);
}

/**
* Get List of Key objects for rows in an iterator using key attribute.
*
* @param iter iterator binding
* @param keyAttrName name of key attribute to use
* @return List of Key objects for rows
*/
public static List keyAttrListForIterator(DCIteratorBinding iter,
String keyAttrName) {
List attributeList = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
attributeList.add(new Key(new Object[] { r.getAttribute(keyAttrName) }));
}
return attributeList;
}

/**
* Get a List of attribute values for an iterator.
*
* @param iter iterator binding
* @param valueAttrName name of value attribute to use
* @return List of attribute values
*/
public static List attributeListForIterator(DCIteratorBinding iter,
String valueAttrName) {
List attributeList = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
attributeList.add(r.getAttribute(valueAttrName));
}
return attributeList;
}

/**
* Find an iterator binding in the current binding container by name.
*
* @param name iterator binding name
* @return iterator binding
*/
public static DCIteratorBinding findIterator(String name) {
DCIteratorBinding iter =
getDCBindingContainer().findIteratorBinding(name);
if (iter == null) {
throw new RuntimeException("Iterator '" + name + "' not found");
}
return iter;
}

/**
* @param bindingContainer
* @param iterator
* @return
*/
public static DCIteratorBinding findIterator(String bindingContainer,
String iterator) {
DCBindingContainer bindings =
(DCBindingContainer)JSFUtils.resolveExpression("#{" +
bindingContainer +
"}");
if (bindings == null) {
throw new RuntimeException("Binding container '" +
bindingContainer + "' not found");
}
DCIteratorBinding iter = bindings.findIteratorBinding(iterator);
if (iter == null) {
throw new RuntimeException("Iterator '" + iterator +
"' not found");
}
return iter;
}

/**
* @param name
* @return
*/
public static JUCtrlValueBinding findCtrlBinding(String name) {
JUCtrlValueBinding rowBinding =
(JUCtrlValueBinding)getDCBindingContainer().findCtrlBinding(name);
if (rowBinding == null) {
throw new RuntimeException("CtrlBinding " + name + "' not found");
}
return rowBinding;
}

/**
* Find an operation binding in the current binding container by name.
*
* @param name operation binding name
* @return operation binding
*/
public static OperationBinding findOperation(String name) {
OperationBinding op =
getDCBindingContainer().getOperationBinding(name);
if (op == null) {
throw new RuntimeException("Operation '" + name + "' not found");
}
return op;
}

/**
* Find an operation binding in the current binding container by name.
*
* @param bindingContianer binding container name
* @param opName operation binding name
* @return operation binding
*/
public static OperationBinding findOperation(String bindingContianer,
String opName) {
DCBindingContainer bindings =
(DCBindingContainer)JSFUtils.resolveExpression("#{" +
bindingContianer +
"}");
if (bindings == null) {
throw new RuntimeException("Binding container '" +
bindingContianer + "' not found");
}
OperationBinding op = bindings.getOperationBinding(opName);
if (op == null) {
throw new RuntimeException("Operation '" + opName + "' not found");
}
return op;
}

/**
* Get List of ADF Faces SelectItem for an iterator binding with description.
*
* Uses the value of the 'valueAttrName' attribute as the key for
* the SelectItem key.
*
* @param iter ADF iterator binding
* @param valueAttrName name of value attribute to use for key
* @param displayAttrName name of the attribute from iterator rows to display
* @param descriptionAttrName name of the attribute for description
* @return ADF Faces SelectItem for an iterator binding with description
*/
public static List selectItemsForIterator(DCIteratorBinding iter,
String valueAttrName,
String displayAttrName,
String descriptionAttrName) {
List selectItems = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
selectItems.add(new SelectItem(r.getAttribute(valueAttrName),
(String)r.getAttribute(displayAttrName),
(String)r.getAttribute(descriptionAttrName)));
}
return selectItems;
}

/**
* Get List of ADF Faces SelectItem for an iterator binding.
*
* Uses the value of the 'valueAttrName' attribute as the key for
* the SelectItem key.
*
* @param iter ADF iterator binding
* @param valueAttrName name of value attribute to use for key
* @param displayAttrName name of the attribute from iterator rows to display
* @return ADF Faces SelectItem for an iterator binding
*/
public static List selectItemsForIterator(DCIteratorBinding iter,
String valueAttrName,
String displayAttrName) {
List selectItems = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
selectItems.add(new SelectItem(r.getAttribute(valueAttrName),
(String)r.getAttribute(displayAttrName)));
}
return selectItems;
}

/**
* Get List of ADF Faces SelectItem for an iterator binding.
*
* Uses the rowKey of each row as the SelectItem key.
*
* @param iteratorName ADF iterator binding name
* @param displayAttrName name of the attribute from iterator rows to display
* @return ADF Faces SelectItem for an iterator binding
*/
public static List selectItemsByKeyForIterator(String iteratorName,
String displayAttrName) {
return selectItemsByKeyForIterator(findIterator(iteratorName),
displayAttrName);
}

/**
* Get List of ADF Faces SelectItem for an iterator binding with discription.
*
* Uses the rowKey of each row as the SelectItem key.
*
* @param iteratorName ADF iterator binding name
* @param displayAttrName name of the attribute from iterator rows to display
* @param descriptionAttrName name of the attribute for description
* @return ADF Faces SelectItem for an iterator binding with discription
*/
public static List selectItemsByKeyForIterator(String iteratorName,
String displayAttrName,
String descriptionAttrName) {
return selectItemsByKeyForIterator(findIterator(iteratorName),
displayAttrName,
descriptionAttrName);
}

/**
* Get List of ADF Faces SelectItem for an iterator binding with discription.
*
* Uses the rowKey of each row as the SelectItem key.
*
* @param iter ADF iterator binding
* @param displayAttrName name of the attribute from iterator rows to display
* @param descriptionAttrName name of the attribute for description
* @return ADF Faces SelectItem for an iterator binding with discription
*/
public static List selectItemsByKeyForIterator(DCIteratorBinding iter,
String displayAttrName,
String descriptionAttrName) {
List selectItems = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
selectItems.add(new SelectItem(r.getKey(),
(String)r.getAttribute(displayAttrName),
(String)r.getAttribute(descriptionAttrName)));
}
return selectItems;
}

/**
* Get List of ADF Faces SelectItem for an iterator binding.
*
* Uses the rowKey of each row as the SelectItem key.
*
* @param iter ADF iterator binding
* @param displayAttrName name of the attribute from iterator rows to display
* @return List of ADF Faces SelectItem for an iterator binding
*/
public static List selectItemsByKeyForIterator(DCIteratorBinding iter,
String displayAttrName) {
List selectItems = new ArrayList();
for (Row r : iter.getAllRowsInRange()) {
selectItems.add(new SelectItem(r.getKey(),
(String)r.getAttribute(displayAttrName)));
}
return selectItems;
}

/**
* Find the BindingContainer for a page definition by name.
*
* Typically used to refer eagerly to page definition parameters. It is
* not best practice to reference or set bindings in binding containers
* that are not the one for the current page.
*
* @param pageDefName name of the page defintion XML file to use
* @return BindingContainer ref for the named definition
*/
private static BindingContainer findBindingContainer(String pageDefName) {
BindingContext bctx = getDCBindingContainer().getBindingContext();
BindingContainer foundContainer =
bctx.findBindingContainer(pageDefName);
return foundContainer;
}

/**
* @param opList
*/
public static void printOperationBindingExceptions(List opList) {
if (opList != null && !opList.isEmpty()) {
for (Object error : opList) {
LOGGER.severe(error.toString());
}
}
}
}

ADF_046:ADF工具类:JSFUtil.java 源代码


package com.javaneverdie.adfdemo.table.view.util;

import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.el.ELContext;
import javax.el.ExpressionFactory;

import javax.el.MethodExpression;
import javax.el.ValueExpression;

import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import javax.servlet.http.HttpServletRequest;


/**
* General useful static utilies for working with JSF.
* NOTE: Updated to use JSF 1.2 ExpressionFactory.
*
* @author Duncan Mills
* @author Steve Muench
*
* $Id: JSFUtils.java 1885 2007-06-26 00:47:41Z ralsmith $
*/
public class JSFUtils {

private static final String NO_RESOURCE_FOUND = "Missing resource: ";

/**
* Method for taking a reference to a JSF binding expression and returning
* the matching object (or creating it).
* @param expression EL expression
* @return Managed object
*/
public static Object resolveExpression(String expression) {
FacesContext facesContext = getFacesContext();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp =
elFactory.createValueExpression(elContext, expression,
Object.class);
return valueExp.getValue(elContext);
}

/**
* @return
*/
public static String resolveRemoteUser() {
FacesContext facesContext = getFacesContext();
ExternalContext ectx = facesContext.getExternalContext();
return ectx.getRemoteUser();
}

/**
* @return
*/
public static String resolveUserPrincipal() {
FacesContext facesContext = getFacesContext();
ExternalContext ectx = facesContext.getExternalContext();
HttpServletRequest request = (HttpServletRequest)ectx.getRequest();
return request.getUserPrincipal().getName();
}

/**
* @param expression
* @param returnType
* @param argTypes
* @param argValues
* @return
*/
public static Object resolveMethodExpression(String expression,
Class returnType,
Class[] argTypes,
Object[] argValues) {
FacesContext facesContext = getFacesContext();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
MethodExpression methodExpression =
elFactory.createMethodExpression(elContext, expression, returnType,
argTypes);
return methodExpression.invoke(elContext, argValues);
}

/**
* Method for taking a reference to a JSF binding expression and returning
* the matching Boolean.
* @param expression EL expression
* @return Managed object
*/
public static Boolean resolveExpressionAsBoolean(String expression) {
return (Boolean)resolveExpression(expression);
}

/**
* Method for taking a reference to a JSF binding expression and returning
* the matching String (or creating it).
* @param expression EL expression
* @return Managed object
*/
public static String resolveExpressionAsString(String expression) {
return (String)resolveExpression(expression);
}

/**
* Convenience method for resolving a reference to a managed bean by name
* rather than by expression.
* @param beanName name of managed bean
* @return Managed object
*/
public static Object getManagedBeanValue(String beanName) {
StringBuffer buff = new StringBuffer("#{");
buff.append(beanName);
buff.append("}");
return resolveExpression(buff.toString());
}

/**
* Method for setting a new object into a JSF managed bean
* Note: will fail silently if the supplied object does
* not match the type of the managed bean.
* @param expression EL expression
* @param newValue new value to set
*/
public static void setExpressionValue(String expression, Object newValue) {
FacesContext facesContext = getFacesContext();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp =
elFactory.createValueExpression(elContext, expression,
Object.class);

//Check that the input newValue can be cast to the property type
//expected by the managed bean.
//If the managed Bean expects a primitive we rely on Auto-Unboxing
Class bindClass = valueExp.getType(elContext);
if (bindClass.isPrimitive() || bindClass.isInstance(newValue)) {
valueExp.setValue(elContext, newValue);
}
}

/**
* Convenience method for setting the value of a managed bean by name
* rather than by expression.
* @param beanName name of managed bean
* @param newValue new value to set
*/
public static void setManagedBeanValue(String beanName, Object newValue) {
StringBuffer buff = new StringBuffer("#{");
buff.append(beanName);
buff.append("}");
setExpressionValue(buff.toString(), newValue);
}


/**
* Convenience method for setting Session variables.
* @param key object key
* @param object value to store
*/
public static

void storeOnSession(String key, Object object) {
FacesContext ctx = getFacesContext();
Map sessionState = ctx.getExternalContext().getSessionMap();
sessionState.put(key, object);
}

/**
* Convenience method for getting Session variables.
* @param key object key
* @return session object for key
*/
public static Object getFromSession(String key) {
FacesContext ctx = getFacesContext();
Map sessionState = ctx.getExternalContext().getSessionMap();
return sessionState.get(key);
}

/**
* @param key
* @return
*/
public static String getFromHeader(String key) {
FacesContext ctx = getFacesContext();
ExternalContext ectx = ctx.getExternalContext();
return ectx.getRequestHeaderMap().get(key);
}

/**
* Convenience method for getting Request variables.
* @param key object key
* @return session object for key
*/
public static Object getFromRequest(String key) {
FacesContext ctx = getFacesContext();
Map sessionState = ctx.getExternalContext().getRequestMap();
return sessionState.get(key);
}

/**
* Pulls a String resource from the property bundle that
* is defined under the application <message-bundle> element in
* the faces config. Respects Locale
* @param key string message key
* @return Resource value or placeholder error String
*/
public static String getStringFromBundle(String key) {
ResourceBundle bundle = getBundle();
return getStringSafely(bundle, key, null);
}


/**
* Convenience method to construct a FacesMesssage
* from a defined error key and severity
* This assumes that the error keys follow the convention of
* using _detail for the detailed part of the
* message, otherwise the main message is returned for the
* detail as well.
* @param key for the error message in the resource bundle
* @param severity severity of message
* @return Faces Message object
*/
public static FacesMessage getMessageFromBundle(String key,
FacesMessage.Severity severity) {
ResourceBundle bundle = getBundle();
String summary = getStringSafely(bundle, key, null);
String detail = getStringSafely(bundle, key + "_detail", summary);
FacesMessage message = new FacesMessage(summary, detail);
message.setSeverity(severity);
return message;
}

/**
* Add JSF info message.
* @param msg info message string
*/
public static void addFacesInformationMessage(String msg) {
FacesContext ctx = getFacesContext();
FacesMessage fm =
new FacesMessage(FacesMessage.SEVERITY_INFO, msg, "");
ctx.addMessage(getRootViewComponentId(), fm);
}

/**
* Add JSF error message.
* @param msg error message string
*/
public static void addFacesErrorMessage(String msg) {
FacesContext ctx = getFacesContext();
FacesMessage fm =
new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, "");
ctx.addMessage(getRootViewComponentId(), fm);
}

/**
* Add JSF error message for a specific attribute.
* @param attrName name of attribute
* @param msg error message string
*/
public static void addFacesErrorMessage(String attrName, String msg) {
FacesContext ctx = getFacesContext();
FacesMessage fm =
new FacesMessage(FacesMessage.SEVERITY_ERROR, attrName, msg);
ctx.addMessage(getRootViewComponentId(), fm);
}

// Informational getters

/**
* Get view id of the view root.
* @return view id of the view root
*/
public static String getRootViewId() {
return getFacesContext().getViewRoot().getViewId();
}

/**
* Get component id of the view root.
* @return component id of the view root
*/
public static String getRootViewComponentId() {
return getFacesContext().getViewRoot().getId();
}

/**
* Get FacesContext.
* @return FacesContext
*/
public static FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
/*
* Internal method to pull out the correct local
* message bundle
*/

private static ResourceBundle getBundle() {
FacesContext ctx = getFacesContext();
UIViewRoot uiRoot = ctx.getViewRoot();
Locale locale = uiRoot.getLocale();
ClassLoader ldr = Thread.currentThread().getContextClassLoader();
return ResourceBundle.getBundle(ctx.getApplication().getMessageBundle(),
locale, ldr);
}

/**
* Get an HTTP Request attribute.
* @param name attribute name
* @return attribute value
*/
public static Object getRequestAttribute(String name) {
return getFacesContext().getExternalContext().getRequestMap().get(name);
}

/**
* Set an HTTP Request attribute.
* @param name attribute name
* @param value attribute value
*/
public static void setRequestAttribute(String name, Object value) {
getFacesContext().getExternalContext().getRequestMap().put(name,
value);
}

/*
* Internal method to proxy for resource keys that don't exist
*/

private static String getStringSafely(ResourceBundle bundle, String key,
String defaultValue) {
String resource = null;
try {
resource = bundle.getString(key);
} catch (MissingResourceException mrex) {
if (defaultValue != null) {
resource = defaultValue;
} else {
resource = NO_RESOURCE_FOUND + key;
}
}
return resource;
}

/**
* Locate an UIComponent in view root with its component id. Use a recursive way to achieve this.
* @param id UIComponent id
* @return UIComponent object
*/
public static UIComponent findComponentInRoot(String id) {
UIComponent component = null;
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext != null) {
UIComponent root = facesContext.getViewRoot();
component = findComponent(root, id);
}
return component;
}

/**
* Locate an UIComponent from its root component.
* Taken from http://www.jroller.com/page/mert?entry=how_to_find_a_uicomponent
* @param base root Component (parent)
* @param id UIComponent id
* @return UIComponent object
*/
public static UIComponent findComponent(UIComponent base, String id) {
if (id.equals(base.getId()))
return base;

UIComponent children = null;
UIComponent result = null;
Iterator childrens = base.getFacetsAndChildren();
while (childrens.hasNext() && (result == null)) {
children = (UIComponent)childrens.next();
if (id.equals(children.getId())) {
result = children;
break;
}
result = findComponent(children, id);
if (result != null) {
break;
}
}
return result;
}

/**
* Method to create a redirect URL. The assumption is that the JSF servlet mapping is
* "faces", which is the default
*
* @param view the JSP or JSPX page to redirect to
* @return a URL to redirect to
*/
public static String getPageURL(String view) {
FacesContext facesContext = getFacesContext();
ExternalContext externalContext = facesContext.getExternalContext();
String url =
((HttpServletRequest)externalContext.getRequest()).getRequestURL().toString();
StringBuffer newUrlBuffer = new StringBuffer();
newUrlBuffer.append(url.substring(0, url.lastIndexOf("faces/")));
newUrlBuffer.append("faces");
String targetPageUrl = view.startsWith("/") ? view : "/" + view;
newUrlBuffer.append(targetPageUrl);
return newUrlBuffer.toString();
}

}

ADF_045:ADF与JSF的区别(翻译+整理)

经常被问到ADF与JSF的区别,二者的主要区别是在模型层的实现上,以下做一简要说明。

In a typical JSF application, you would create objects like the userInfoBean object as a managed bean.
在一个典型的JSF应用中,你会使用类似于userInfoBean这样的对象作为Managed Bean。
The JSF runtime manages instantiating these beans on demand when any EL expression references them for the first time.
JSF在运行时会根据情况初始化这些Beans,比如EL表达式中索引到了这些Beans。
However, in an application that uses the ADF Model layer, instead of binding the UI component attributes to properties or methods on managed beans, JDeveloper automatically binds the UI component attributes to the ADF Model layer, which uses XML configuration files that drive generic data binding features.
然而,一个使用了ADF Model的应用,不是把UI组件绑定到Managed Bean的属性或方法上,而是将其绑定到ADF Moel层:使用XML配置文件来驱动数据绑定关系。
It implements the two concepts in JSR-227 that enable decoupling the user interface technology from the business service implementation: data controls and declarative bindings.
它使用了JSR-227标准中的两个概念:Data Control和声明式的Data Binding,从而解耦了UI层和业务服务层,即UI组件与业务服务层的实现无关。

In the model layer, Oracle ADF Model implements the JSR-227 service abstraction called the data control.
在模型层,ADF Model实现了JSR-227服务抽象标准:Data Control。
Data controls abstract the implementation technology of a business service by using standard metadata interfaces to describe the service’s operations and data collections, including information about the properties, methods,and types involved.
Data Control通过使用标准的元数据接口来描述服务的操作和数据集合(包括属性、方法、数据类型),从而抽象了业务服务层的实现。
Using JDeveloper, you can view that information as icons that you can then drag and drop onto a page.
At that point, JDeveloper automatically creates the bindings from the page to the services.
使用JDeveloper,你可以拖放Data Control到页面上,创建UI组件的同时生成与业务服务的绑定关系。
At runtime, the ADF Model layer reads the information describing your data controls and data bindings from appropriate XML files and implements the two-way connection between your user interface and your business service.
在运行时,ADF Model层通过读取一些XML文件来获得Data Control和Data Binding信息,从而在UI层和业务服务层之间建立了“双向连接”。

2009年9月27日星期日

ADF_044:如何在ADF页面中显示警告信息?

本文最后一次修改时间: 2012-01-19。
运行环境:JDeveloper 11.1.2.1.0 + Oracle Database 10g Express Edition 10.2.0.1。

我以《使用ADF实现基于Form的CRUD (3)》的项目代码为例,展示如何在ADF页面中显示警告信息。

1. 显示在某个组件的旁边
要想显示在组件的旁边,首先要得到这个组件的id或clientId,然后就是构造FacesMessage,并显示出来。

1.1 组件已经绑定在Managed Bean中
为某个组件设置Binding的方法如下:
选中该组件,找到Binding属性,然后选择或创建Managed Bean和属性名称。


我这里选择了两个组件,一个是按钮,一个是InputText。
修改lastButton_actionListener方法如下:

public void lastButton_actionListener(ActionEvent actionEvent) {
if (!isDirty()) {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Last");
Object result = operationBinding.execute();
} else {
//popupMessage(lastButton.getId(),Save_Or_Undo_Message);
popupMessage(maxSalaryInputText.getId(),Save_Or_Undo_Message);
//popupMessage(actionEvent,Save_Or_Undo_Message);
}
}

public void popupMessage(String clientId, String message) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
}

测试结果:使用按钮时,不知为何警告信息始终显示在页面中央;使用InputText时,警告信息显示在组件旁边。
使用按钮时,

使用InputText时,


1.2 通过组件上的事件获取组件


public void lastButton_actionListener(ActionEvent actionEvent) {
if (!isDirty()) {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Last");
Object result = operationBinding.execute();
} else {
//popupMessage(lastButton.getId(),Save_Or_Undo_Message);
//popupMessage(maxSalaryInputText.getId(),Save_Or_Undo_Message);
popupMessage(actionEvent,Save_Or_Undo_Message);
}
}

public void popupMessage(ActionEvent actionEvent, String message) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(actionEvent.getComponent().getClientId(context),
new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
}

测试结果和1.1一样:使用按钮时,不知为何警告信息始终显示在页面中央;使用InputText时,警告信息显示在组件旁边。

1.3 使用JSFUtils.getRootViewComponentId 获取根组件id

public void lastButton_actionListener(ActionEvent actionEvent) {
if (!isDirty()) {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Last");
Object result = operationBinding.execute();
} else {
//popupMessage(lastButton.getId(),Save_Or_Undo_Message);
//popupMessage(maxSalaryInputText.getId(),Save_Or_Undo_Message);
//popupMessage(actionEvent,Save_Or_Undo_Message);
popupMessage(JSFUtils.getRootViewComponentId(),Save_Or_Undo_Message);
}
}


1.4 使用JSFUtils.findComponentInRoot获取根组件对象

public void lastButton_actionListener(ActionEvent actionEvent) {
if (!isDirty()) {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Last");
Object result = operationBinding.execute();
} else {
System.out.println(JSFUtils.getRootViewComponentId());
//popupMessage(lastButton.getId(),Save_Or_Undo_Message);
//popupMessage(maxSalaryInputText.getId(),Save_Or_Undo_Message);
//popupMessage(actionEvent,Save_Or_Undo_Message);
//popupMessage(JSFUtils.findComponentInRoot(lastButton.getId()).getClientId(),Save_Or_Undo_Message);
popupMessage(JSFUtils.findComponentInRoot(maxSalaryInputText.getId()).getClientId(),Save_Or_Undo_Message);

}
}


2. 显示在页面中央,以弹出窗口的形式,适用于全局性的重要提示或错误警告
只要调用addMessage时,第一个参数赋值为null就可以了,其它的代码不变。

public void popupMessage(String message) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
}


Project 下载:Form_CRUD(popupMessage).7z

参考文献:
1. http://adfjsf.blogspot.com/2009/08/programmatic-error-message-in-adf-11g.html
2. http://mukx.blogspot.com/2009/01/adf-10g-how-to-show-faces-message-both.html

ADF_043:使用ADF实现基于Form的CRUD (3)

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

接上一个Project,在测试Update功能时,发现了一个小问题:
当用户修改数据项后,点击导航按钮时,应该提示:“数据已改动,请先保存或者放弃。”。

问题分析:
(1)当用户修改数据项并点击导航按钮后,相当于点击了Submit按钮,这时数据改动已经提交到了模型层,Application Module可以感知到模型层数据发生了修改。
(2)Commit按钮的disabled属性="#{!bindings.Commit.enabled}",我们也可以通过判断Commit按钮是否Enable了,来判断数据是否发生了改变。

重要步骤说明:

1. 复制JSFUtil.java和ADFUtil.java到View Controller Project。

2. 把Managed Bean的一些工具方法总结出来,创建一个BaseBackingBean。
其它BackingBean继承BaseBackingBean。
在本文中用到的方法有:

public BindingContainer getBindings() {
return BindingContext.getCurrent().getCurrentBindingsEntry();
}

public boolean isDirty() {
ApplicationModule am = ADFUtils.getDCBindingContainer().getDataControl().getApplicationModule();
return am.getTransaction().isDirty();
}

public boolean isCommitEnabled() {
Boolean commitState = (Boolean)JSFUtils.getManagedBeanValue("bindings.Commit.enabled");
boolean commitEnabled = commitState != null ? commitState.booleanValue() : false;
return commitEnabled;
}

public void popupMessage(String message) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
}

public void popupMessage(ActionEvent actionEvent, String message) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(actionEvent.getComponent().getClientId(context),
new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
}


3. 使用Java代码调用First、Previous、Next、Last Operation
方式和调用Delete Operation一样,都是通过BindingContainer找到OperationBinding。
这里只列出调用First Operation的代码:

public void firstButton_actionListener(ActionEvent actionEvent) {
if (!isDirty()) {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("First");
Object result = operationBinding.execute();
} else {
popupMessage(actionEvent,Save_Or_Undo_Message);
}
}

First按钮的页面代码如下:
<af:commandButton text="First" disabled="#{!bindings.First.enabled}" partialSubmit="true" id="cb1"
actionListener="#{myBackingBean.firstButton_actionListener}"/>

4. 运行
修改一条记录,然后点击导航按钮,会弹出提示窗口,如下图:

注意,这里有一个问题,就是不知道为什么,弹出的信息不能显示在点击按钮的附近,而是只能显示在页面中央?
进一步实验发现,如果组件是输入型组件,比如InputText,就可以显示在旁边,如下图:

难道context.addMessage方法的第一个参数对命令型组件不起作用?

Project 下载: Form_CRUD(3).7z

2009年9月26日星期六

ADF_042:使用ADF实现基于Form的CRUD (2)

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

接上一个Project,在测试Delete功能时,发现了两个小问题。
(1)点击Delete时应该有提示,当用户确认后再删除
(2)点击Delete后,不需要再点击Commit按钮提交。
好,我们现在就把Delete功能按照上面的要求修改一下。

重要步骤说明:

1. 拖放Popup组件到Delete按钮后面,拖放Dialog组件到Popup组件中,拖放OutputText组件到Dialog组件中
弹出的窗口是使用Popup组件来实现的,弹出窗口的内容是用Dialog组件来实现的,提示信息是用OutputText来实现的。
把popup的id="popupDelete",供后面引用。

2. 拖放Show Popup Behavior Operation到Delete按钮上
Show Popup Behavior是用来触发Popup组件的,设置其属性如下图:


3. 使用dialogListener来监听用户选择了“确定”or“取消”
找到Dialog组件,设置其dialogListener属性,选择新Managed Bean和方法deleteDialogListener。



其中deleteDialogListener方法,代码如下:

public void deleteDialogListener(DialogEvent dialogEvent) {
if (dialogEvent.getOutcome().equals(DialogEvent.Outcome.ok)){
doDelete();
}
}

至于doDelete方法,我们在下一步定义。

4. 使用Java代码调用Delete按钮
(1)查看Delete按钮的页面源码,内容如下:
<af:commandButton actionListener="#{bindings.Delete.execute}" text="Delete" disabled="#{!bindings.Delete.enabled}" id="cb7">
<af:showPopupBehavior popupId="popupDelete" triggerType="action" align="endAfter"/>
</af:commandButton>
(2)双击Delete按钮,选择如下:

确定后,会生成代码如下:

public BindingContainer getBindings() {
return BindingContext.getCurrent().getCurrentBindingsEntry();
}

public String cb7_action() {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Delete");
Object result = operationBinding.execute();
if (!operationBinding.getErrors().isEmpty()) {
return null;
}
return null;
}


Delete按钮页面代码变为:
<af:commandButton text="Delete" action="#{myBackingBean.cb7_action}" disabled="#{!bindings.Delete.enabled}" id="cb7">
<af:showPopupBehavior popupId="popupDelete" triggerType="action" align="endAfter"/>
</af:commandButton>

(3)把cb7_action方法的代码复制到doDelete方法中,并做一定的修改,最终doDelete方法内容如下:

private void doDelete() {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Delete");
Object result = operationBinding.execute();
if (operationBinding.getErrors().isEmpty()) {
operationBinding = bindings.getOperationBinding("Commit");
operationBinding.execute();
}
}


(4)删除cb7_action方法,同时删除页面中Delete按钮的action属性,因为真正的Delete操作已经dialogListener完成了。
最终的Delete页面代码如下:
<af:commandButton text="Delete" disabled="#{!bindings.Delete.enabled}" id="cb7">
<af:showPopupBehavior popupId="popupDelete" triggerType="action" align="endAfter"/>
</af:commandButton>
<af:popup childCreation="deferred" autoCancel="disabled" id="popupDelete">
<af:dialog id="d2" title="Confirm Delete"
dialogListener="#{myBackingBean.deleteDialogListener}">
<f:facet name="buttonBar"/>
<af:outputText value="The record will be deleted, are you sure?" id="ot1"/>
</af:dialog>
</af:popup>

5. 运行
点击Delete按钮,会提示是否删除记录,确定后,记录将被删除,无需再点击Commit提交。


Project 下载: Form_CRUD(2).7z

ADF_041:使用ADF实现基于Form的CRUD (1)

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

注意,这里的CRUD指的是Create、Read、Update、Delete,没有查询(Query)功能。

重要步骤说明:

1. 创建 Fusion ADF Web Application

2. 在Model Project中,选择Jobs表,并为其创建EO和VO

3. 在View Controller Project中,创建页面:form_crud.jsf
(1)从Data Control中拖放JobVO1到页面上,选择生成Form。

(2)从Data Control中拖放CreateInsert、Delete、Commit、Rollback Operation到页面上,选择生成Button。
(3)把Submit按钮、CreateInsert按钮、Delete按钮放在一个Panel Group Layout中。
(4)为Form加一个“外套”:Panel Box,注意把其Type属性=stretch,这样它会根据Form大小显示,看起来比较美观。
(5)把Commit按钮和Rollback按钮,放到Panel Box中的Toolbar中。
完成后,页面看起来的样子如下:


4. 运行
(1)Create功能:点击CreateInsert按钮,可以增加新的记录,点击Commit按钮提交数据到数据库。
(2)Read功能:点击导航按钮,可以查看不同的记录。
(3)Update功能:可以修改一些记录,然后点击Commit按钮提交数据到数据库。
(4)Delete功能:点击Delete按钮,可以删除记录,注意,需要点击Commit按钮提交数据到数据库。

至此,我们使用ADF实现了一个基于Form的CRUD的全部功能,怎么样,不难吧。

Project 下载: Form_CRUD(1).7z

2009年9月21日星期一

ADF_040:如何查看和设置ADF日志

1. 查看 ADF BC 日志
(1)把Debug日志信息写到控制台
双击打开Model项目,选择“Run/Debug/Profile”项,在“Run Configurations”中选中Default,点击“Edit”按钮,在Java Options中写入-Djbo.debugoutput=console。
这样日志窗口中就会打印出很详细的诊断信息,主要包括:
  • 操作系统信息

  • Java虚拟机信息

  • 创建BindingContext信息

  • 装载BC4J属性

  • 创建数据库连接信息

  • 创建业务实体信息

(2)右键点击AppModule,选择“Debug”,随着操作一些信息会打印到控制台,如SQL 语句之类的信息。
(3)添加自己的日志信息。
import oracle.adf.share.logging.ADFLogger;
import oracle.jbo.server.ViewObjectImpl;
public class EmployeesViewImpl extends ViewObjectImpl {
private static ADFLogger logger = ADFLogger.createADFLogger(EmployeesViewImpl.class);
public EmployeesViewImpl() {
logger.info("EmployeesViewImpl ADFLogger Test!");
}
}
你的信息会以红色的字体输出,非常醒目。

2. 查看 ADF Web 应用日志(1)同上(1)。
(2)设置完第一步后,还不能打印出日志信息,因为还需要设置Log 级别。
打开logging.xml,它位于形如:C:\JDeveloper\system11.1.1.3.37.56.60\DefaultDomain\config\fmwconfig\servers\DefaultServer目录下。
你可以在JDeveloper通过如下方式可视化修改此文件:打开菜单
View -> Application Server Navigator,右键点击IntegratedWebLogicServer,选择 Configure Oracle Diagnostic Logging for “IntegratedWebLogicServer”。
设置你的Log 级别, 一般来说,根据package来设置比较好,否则信息太多不好找,速度也慢。
比如把package以oracle打头的级别设置为Info,对应Java Log Levels;或者Notification:1,对应ODL Log Levels。
注意,每次修改logging.xml文件后,需要重启WLS。
(3)同上(3)。
你的信息会以蓝色的字体输出,与其它日志混在一起,不知道为啥这里不是红色的。

3. 使用Oracle Diagnostic Log Analyzer 分析日志


参考文献:
1. http://www.oracle.com/technology/products/jdev/tips/muench/debugger/index.html
2. http://jdeveloperfaq.blogspot.com/2009/12/faq-6-how-to-fine-tune-adf-diagnostics.html
3. http://eleven-china.blogspot.com/2009/01/adf-logger.html
4. http://jdeveloperfaq.blogspot.com/2010/01/faq-8-how-to-perform-log-analysis-using.html
5. http://oracleseeker.com/2008/11/20/debugging_adf_business_component_by_adflogger/
6. http://oracleseeker.com/2008/11/30/debugging_configuration_logging_for_adflogger/

ADF_039:页面初次Load时是否调用了查询方法?

开发环境:JDevloper 11.1.2.1.0+ Oracle Database 10g Express Edition 10.2.0.1。

在页面首次Load时是否调用了查询方法?对此,有人在论坛上争论不休。
在这里,我们使用《ADF开发初体验之二:使用EJB、JPA、JSF开发Web应用》的现成的应用HR_EJB_JPA验证一下。

重要步骤说明:

1. 打开HR_EJB_JPA应用,新建一个页面:testPageFirstLoading.jsf

2. 从Data Control中拖放getEmployeesFindBySal方法的名称到页面,生成查询表单

3. 从Data Control中拖放getEmployeesFindBySal方法的返回值Employees到页面,生成查询结果Table

完成后,看起来是这个样子,和普通的查询界面没啥区别。


4. 在Model Project中,找到HRFacadeBean.java
找到getEmployeesFindBySal方法,在返回语句前,加一句Log。
因为查询方法如果被执行,一定会调用该方法,所以我们在这里加上Log,看看页面初次Load是否打出了该Log。
/** <code>select o from Employees o where o.salary > :p_sal</code> */
public List<Employees> getEmployeesFindBySal(BigDecimal p_sal) {
System.out.println("Hi, I am in getEmployeesFindBySal(BigDecimal p_sal).");
return em.createNamedQuery("Employees.findBySal").setParameter("p_sal", p_sal).getResultList();
}

5. 运行testPageFirstLoading.jsf
页面Load完毕后,我们清楚地看到日志中有一行话:Hi, I am in getEmployeesFindBySal(BigDecimal p_sal).

这说明页面初次Load时确实调用了查询方法,不符合一般界面功能的要求。

6. 如何让页面初次Load时不调用了查询方法?
按照逻辑,页面初次Load时,参数为null,此时不应该执行查询。
在本例中,输入参数是p_sal,其不为空的EL表达式为:#{bindings.p_sal.inputValue ne null}。
因此,在testPageFirstLoading.jsf页面中,选择Bindings Tab,选择getEmployeesFindBySalIterator,设置其RefreshCondition为:#{bindings.p_sal.inputValue ne null}。

重新运行页面,发现页面初次Load时没有再打印任何语句,说明没有执行查询方法。
注意,RefreshCondition设置为#{!adfFacesContext.initialRender}是不起作用的,只有在使用ADF-BC实现业务服务层时才起作用。具体原因尚待查明。

问题1:本例是使用EJB作为模型层来验证该问题的,不知道使用ADF-BC作为模型层会怎样?
答:我认为使用ADF-BC作为模型层,页面初次Load时也会调用查询“方法”。
因为不管模型层使用的是哪种技术,都是用的ADF Data Control生成的绑定关系。
当然,这需要进一步验证,因为ADF-BC隐藏了实现细节,因此我们不知道它是如何实现查询的。也就没有办法在某个方法中加Log来判断。
等我对ADF-BC实现细节有了解以后,再来彻底验证这个问题。
当然,如果能够看到查询的SQL 语句,也能说明执行了查询。

问题2:根据参考文献中的说明:
在ADF10g中,设置${adfFacesContext.postback == true}作为Iterator的刷新条件。
在ADF11g中,设置#{!adfFacesContext.initialRender}作为Iterator的刷新条件。

我测试的结果是不是所有的情况都适用,比如对form组件起作用,对table组件不起作用,因此使用时请务必测试。

Project 下载: HR_EJB_JPA PageLoad.7z

参考文献:
1. https://blogs.oracle.com/shay/entry/delay_method_execution_when_us
2. https://blogs.oracle.com/shay/entry/preventing_queries_when_page_f
3. http://www.youtube.com/watch?v=704w8786i14
4. http://adfdevelopers.blogspot.com/2009/06/preventing-execution-of-queries-when.html

2009年9月20日星期日

ADF_038:开发ADF桌面应用

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

完成《Getting Started with ADF Desktop Integration








ADF_037:ADF开发初体验之二:使用EJB、JPA、JSF开发Web应用

本文最近一次修改时间为 2011-09-30,本实验使用JDeveloper 11.1.2.1.0重新开发。
开发环境:JDeveloper 11.1.2.1.0。
完成《Build a Web Application with JDeveloper 11g Using EJB, JPA, and JavaServer Faces》。

学习要点:

1. 从数据库表创建JPA Entities,生成相应的java文件,主要包括Entities的属性和CRUD方法。
以后可以根据需要,手工添加一些方法。
关于JPA的详细信息,可以参考《JPA是个啥东东?》。




2. 创建EJB Session Bean,选择Persistence Type=JPA,选择暴露哪些Entities上的方法。
Entity如果增加了新的方法想要通过Session Bean 调用,右键点击EJB Session Bean,选择Edit Session Facade,把新方法加进来即可,然后重新生成Data Control。
创建SessionFacade Bean用来访问EJB或Entities的,其它Java Client不能直接访问Entities,而只能通过SessionFacade Bean,这是一种很好的设计模式。
这样设计的好处是:
(1)“面向接口”设计。
Client端无需依赖业务对象(其它EJB或Entities)的实现,即使接口改变,也可以做到Client不变,修改SessionFacade Bean相对来说比修改Client要容易很多。
(2)减少远程通信。
因为其它EJB或Entities都是部署在Server端的,如果Client端直接访问EJB或Entities,将会有大量的远程通讯,网络负担很重。
而SessionFacade Bean也是部署在Server端的,这样就避免了不必要的网络传输,当然这里有一个粒度设计的问题。
关于SessionFacade设计模式,请参考《门面模式 Facade》。


指定SessionFacade Bean要实现的接口:
(1)Remote Interface:客户端程序与SessionFacade Bean运行在不同的JVM中,比如Java Standalone Client。
(2)Local Interface:客户端程序与SessionFacade Bean运行在同一个的JVM中,比如Web Client。



3. 右键SessionFacade Bean,生成Data Control
这时,所有暴露在Local Interface的方法,将会出现在Data Control中。


4. 测试Session Bean
测试Session Bean非常简单 ,右键点击EJB Session Bean,选择New Sample Java Client,就会帮你生成测试代码,你只需要传一些必要的参数即可。
测试前,先要运行Session Bean(把Session Bean 发布到WLS上,并启动),然后右键点击Java Client,选择Run。



5. 在Java EE Container外部创建JPA Entities
JPA Entities 也可以不在Java EE Container中运行,在Java SE Container中也可以调用。
你需要创建一个新的persistence unit,右键点击persistence.xml选择New Java Service Facade,选择Persistence Type=JPA,选择暴露哪些Entities上的方法。
右键persistence.xml,创建Client。
在main函数中,可以增加你的测试代码,并直接运行——不需要先运行Session Bean。


6. 创建页面时,拖放persist方法提交数据
这里有两个提交数据库方法:persist和merge。
注意,persist实际上做的操作是Insert,因此只能用于新建一个对象,然后调用persist提交。
如果主键重复,会报错。
而merge会检查主键是否存在,如果存在,执行Update操作;否则执行Insert操作,然后提交。
参数设置如下:


7. 创建Taskflow时,使用PageflowScope在两个页面之间传递数据
Find按钮上使用Set Action Listener传递参数。
其中,From参数来自bindings中的参数;To参数指定到pageFlowScope.sal中。
注意,To参数可以直接这样写,无需提前在其它地方定义,即表明在PageFlowScope中有一个变量sal。

这是一个非常典型的在两个页面之间传递参数的方式:
(1)首先有一个带返回值和输入参数的方法:比如List getEmployeesFindBySal(BigDecimal p_sal)。
(2)在Data Control 中拖放该方法名称到第1个页面,生成查询表单。
(3)在查询按钮上,增加SetPropertyListener,把参数传递到PageFlowScope中。
(4)在Data Control中拖放该方法的返回值名称到第2个页面,生成查询结构列表。
因为该方法有输入参数,所以拖放时会提示你定义输入参数来自哪里,这里设置成pageFlowScope.sal。
即在第1个页面中保存的PageFlowScope变量sal。

参考文献:
1. http://blogs.oracle.com/adf/entry/difference_between_ejb_persist_merge_operation
2. http://blogs.oracle.com/shay/entry/insert_update_for_jpaejb_30_wi

Project 下载:HR_EJB_JPA.7z