当前位置: 代码迷 >> JavaScript >> 使JSF (ADF Faces) 支持动态领航
  详细解决方案

使JSF (ADF Faces) 支持动态领航

热度:335   发布时间:2012-10-26 10:30:59.0
使JSF (ADF Faces) 支持动态导航
背景介绍
在使用标准的 JSF Navigation 时需要为 指定确定的 URL 地址。如果为其配置 EL expression 会在运行时找不到页面,究其原因是因为没有对 EL 进行相应的赋值转化,即没有转化为 EL 所指定的 URL 地址。

项目中遇到如下问题:需要使 ADF Faces (不想对这个东西做更多的介绍,如果不是很清楚可以去 google 一把 )支持动态的导航,因为原有的好多导航链接都直接存放在了数据库中,而新系统必须依赖旧系统中的数据库(可以新添数据项,但不能删除已有的数据)。

实现原理
非常幸运地在网络上找到这篇文章 How to make JSF navigation rules more dynamic,其解决了基于 JSF 的解决方案,但是其针对的实现版本是 SUN 的官方实现。出于总结的目的,下文会把这篇文章的概要和实现在这里描述一下。

后来参读 JavaServer Faecs in Action (如想理解机制和原理,推荐这本书;如仅仅想快速进入开发,推荐 Core JavaServer Faces 2nd),发现原因主要集中在 ViewHandler 身上,因为在书中如此描述说明该类:
"The default Navigation-Handler instance selects a new view identifier based on the outcome and any configured navigation rules, uses the ViewHandler to create the new view, and sets it as the current view on the FacesContext. The selected view is then displayed by the ViewHandler during the Render Response phase of the Request Processing Lifecycle. The default ViewHandler handles this by forwarding the request to the web container, which processes the JSP page normally."
"The ViewHandler class is used internally by JSF to create new UIView-Root instances. This class is pluggable, so you can either decorate it or write an entirely new implementation―this is how JSF can support display technologies other than JSP."

现在明白了为什么 JSF 的 Navigation rules 中的 to-view-id 不支持 EL,原因是相应的 ViewHandler 不支持 EL expression 的解释。那么接下来的事情就简单了。

JSF (SUN version)
- 这里的实现来自于上面的那篇文章,没有经过真正的测试

1. ViewHandler实现 - 继承 com.sun.facelets.FaceletViewHandler 并且 overwrite getActionURL().
java 代码
  1. @Override
  2. public String getActionURL(FacesContext context, String viewId)
  3. {
  4. String result = viewId;
  5. if(Util.isVBExpression(viewId))
  6. {
  7. ValueBinding vb = context.getApplication().createValueBinding(viewId);
  8. result = vb.getValue(context).toString();
  9. }
  10. result = super.getActionURL(context, value);
  11. int queryStart = value.indexOf("?");
  12. if((queryStart > 0) && (result.indexOf("?") == -1))
  13. {
  14. result = result + value.substring(queryStart);
  15. }
  16. return result;
  17. }

2. ViewHandler注册 - 注意体会 JSF 的 pluginable 机制的方便之处
xml 代码
  1. <application>
  2. ...
  3. <view-handler>com.interactive_objects.jsf.crud.generic.DynamicViewHandler<!---->view-handler>
  4. <!---->application>

3. Navigation配置
xml 代码
  1. <navigation-rule>
  2. <from-view-id>/pages/organisation/*<!---->from-view-id>
  3. <navigation-case>
  4. <from-action>#{OrganisationBean.select}<!---->from-action>
  5. <from-outcome>selected<!---->from-outcome>
  6. <to-view-id>#{OrgansiationBean.computedRedirect}<!---->to-view-id>
  7. <redirect/>
  8. <!---->navigation-case>
  9. <!---->navigation-rule>

ADF Faces 实现

ADF Faces 的实现和 JSF 的有所不同。在 SUN 的官方实现中,getActionURL 能够得到 to-view-id 的配置值,也就是说,无论你的配置值是什么,该方法都可以接受到。但是在 Oracle 的实现方法中,如果你的配置中包含有 EL expression,该方法是无法接受到配置值的,所以我们需要在 createView 方法中,实现上面的功能。

1. ViewHandler实现
java 代码
  1. package com.xxx.xxx;
  2. import oracle.adfinternal.view.faces.application.ViewHandlerImpl;
  3. import javax.faces.application.ViewHandler;
  4. import javax.faces.context.FacesContext;
  5. import javax.faces.el.ValueBinding;
  6. import javax.faces.component.UIViewRoot;
  7. import com.sun.faces.util.Util;
  8. /**
  9. * This class is a subcalss of oracle.adfinternal.view.faces.application.ViewHandlerImplViewHandlerImple.
  10. * This class will replace the ADF Faces default ViewHandler class to handle the function how to create the view based on
  11. * the element of the .
  12. *
  13. */
  14. public class CeViewHandler extends ViewHandlerImpl {
  15. public CeViewHandler(ViewHandler viewHandler) {
  16. super(viewHandler);
  17. }
  18. @Override
  19. /**
  20. * This method gets the content of the element.
  21. * Then if the content is an expression, the method will translate expression into the real value.
  22. */
  23. public UIViewRoot createView(FacesContext facesContext, String s) {
  24. // Delegate the super to get the rest work done.
  25. return super.createView(facesContext, parseExpression(facesContext, s));
  26. }
  27. @Override
  28. public String getActionURL(FacesContext facesContext, String s) {
  29. return super.getActionURL(facesContext, parseExpression(facesContext, s));
  30. }
  31. private String parseExpression(FacesContext facesContext, String s) {
  32. String result = s;
  33. // If the view-id element is expressin, get the real value.
  34. if (Util.isVBExpression(s)) {
  35. ValueBinding vb = facesContext.getApplication().createValueBinding(s);
  36. result = vb.getValue(facesContext).toString();
  37. }
  38. return result;
  39. }

  40. }

接下来的步骤可以参考上面的 JSF 过程。

感觉 JSF 的 pluginable 机制还是有其某些方面的可取之处的,但是这个 server-state 的机制能够在走多远,我们只能拭目以待。(FYI: ADF Faces 可以实现 client-side state 了,绝不是广告,希望大家在用这个框架是三思,同时可以等 Apache Trinidad 发布正式版本后在去使用。)