我的应用程序(JSF 2、Java 6、JBoss 7.1)必须提供两种操作模式:可访问模式和不可访问模式。
在可访问性模式下,一些(不是所有)页面具有特定的设计,以便屏幕阅读器更好地阅读。这两种模式之间的区别纯粹是视觉上的,托管bean完全相同。理想情况下,不需要更改Java代码。
大部分工作已完成:
- 在页面顶部添加了一个链接来切换模式
- 添加了一个支持bean来处理点击链接
- 当可访问性被打开时,一个属性被添加到会话cookie中,以标记它是可访问的
- 添加了一个资源解析器,当来自特定用户的特定请求的可访问性开启时,将页面路径重写为可访问版本
- 应用程序以非可访问模式启动
- 我沿着一些页面导航
- 我通过点击相应的链接打开无障碍模式
- 我收到一个页面告诉我,我现在在可访问性模式,我注意到菜单已更改为其可访问性版本(不同的组件存在于不同的页面模板)
- 我导航到未访问的页面,它们都处于可访问模式
- 我在打开可访问性之前导航到访问过的页面,我在非可访问性版本 中看到它们
在最后一步中,我们可以理解,即使在可访问模式和资源路径转换发生时(我有日志来证明),页面也会像在默认的非可访问模式下一样生成。
那么,JSF中真的有页面缓存吗?我怎样才能清除它,使页面确实能够再次呈现?
更新1
网络监控显示请求确实发出到应用程序,所以这里没有浏览器缓存。
经过一段时间后,我终于找到了一个解决方案,这意味着,一个满足我所有需求的编码策略。也许它在技术上不是一个好的解决方案,但它是一个功能性的解决方案,因为它产生了我需要提供的体验,节省了我触摸所有已经存在于Java代码中的时间。它开始了!
1。一个托管bean来切换可访问性模式
下面的托管bean负责在用户单击某个命令链接时打开和关闭可访问性。其思想是在可访问性模式打开时向会话添加一个属性,并在可访问性模式关闭时删除它。
这些操作返回当前页面的重定向,以便用户立即看到界面从一种模式切换到另一种模式。
当可访问性打开时,视图路径被更改为附加/ac
,因为所有可访问的视图都具有与不可访问的视图相同的文件名,但位于名为ac
的子目录中。
@ManagedBean
@RequestScoped
public class AccessibilityMB {
private boolean accessibilityOn = false;
@PostConstruct
public void init() {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session!=null) {
Boolean accessibilityMode = (Boolean)session.getAttribute("AccessibilityMode");
accessibilityOn = accessibilityMode!=null && accessibilityMode;
}
}
public String turnAccessibilityOff() {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session!=null) {
session.setAttribute("AccessibilityMode", accessibilityOn = true);
}
String viewId = context.getViewRoot().getViewId();
return viewId+"?faces-redirect=true";
}
public String turnAccessibilityOn() {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session!=null) {
accessibilityOn = false;
session.removeAttribute("AccessibilityMode");
}
String viewId = context.getViewRoot().getViewId();
int index = viewId.lastIndexOf("/ac/");
if(index>-1)
viewId = viewId.substring(0, index)+viewId.substring(index+3);
return viewId+"?faces-redirect=true";
}
public boolean getAccessibilityOn() {
return accessibilityOn;
}
}
2。当可访问性模式为
时,用于纠正视图路径的PhaseListener PhaseListener只是检查可访问性模式,在这种情况下,重写视图在ac
子目录中查找的路径。如果指定的视图存在,则丢弃当前组件树,并从同一视图的可访问版本重新构建。
这里的解决方案不是很好,因为JSF已经构建了一个组件树,而我只是放弃它,从另一个文件中重新构建它。
public class AccessibilityPhaseListener implements PhaseListener{
private static final long serialVersionUID = 1L;
@Override
public void beforePhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session==null) {
return;
}
Boolean acessibilityMode = (Boolean)session.getAttribute("AcessibilityMode");
if(acessibilityMode==null || !acessibilityMode)
return;
String viewId = context.getViewRoot().getViewId();
if(acessibilityMode) {
int index = viewId.lastIndexOf("/");
viewId = viewId.substring(0, index+1)+"ac/"+viewId.substring(index+1);
} else {
int index = viewId.lastIndexOf("/");
if(viewId.substring(index-3, index).equals("/ac"))
viewId = viewId.substring(0, index-3)+viewId.substring(index);
}
URL url = null;
try {
url = context.getExternalContext().getResource(viewId);
} catch (MalformedURLException e) {
}
if(url==null)
return;
ViewHandler handler = context.getApplication().getViewHandler();
UIViewRoot root = handler.createView(context, viewId);
root.setViewId(viewId);
context.setViewRoot(root);
}
@Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
结论我可以满足我所有的要求:
- 无需重构Java代码,应用程序现在可以在两种不同的模式下工作;
- 我只重建了我想要的视图,在原始视图与屏幕阅读器正常工作的情况下,它保持原样;
- 无障碍模式可以在任何时候打开和关闭,用户对这样的动作有立即的反应;
我理解这个解决方案适用于任何类型的应用程序模式,而不仅仅是可访问性。只要有人需要根据应用程序或会话参数选择某个视图而不是另一个视图,它就可以工作。例如,一个多元文化的应用程序,其文化定制远不止于颜色和语言,需要完全重新设计视图,可以利用这个模型。
所有这一切的缺点是,当可访问模式开启并且存在某个视图的可访问版本时,JSF将工作两次,一次构建原始视图,第二次构建可访问版本。