【问题标题】:How to work with application modes in JSF如何在 JSF 中使用应用程序模式
【发布时间】:2017-02-05 05:58:36
【问题描述】:

我的应用程序(JSF 2、Java 6、JBoss 7.1)必须提供两种操作模式:可访问性模式和不可访问性模式。

在辅助功能模式下,某些(并非所有)页面具有特定的设计,以便屏幕阅读器可以更好地阅读。两种模式之间的区别纯粹是视觉上的,托管 bean 完全相同。理想情况下,无需更改 Java 代码。

大部分工作已经完成:

  1. 在页面顶部添加了用于切换模式的链接
  2. 添加了一个支持 bean 来处理对链接的点击
  3. 打开可访问性时,会向会话 cookie 添加一个属性,以将其标记为可访问
  4. 添加了 ResourceResolver 以在针对特定用户的特定请求启用可访问性时将页面路径重写为可访问版本

尽管如此,它几乎完美地工作,但似乎有某种视图缓存破坏了我的解决方案。考虑以下场景:

  1. 应用程序以不可访问模式启动
  2. 我浏览了一些页面
  3. 我通过点击相应的链接打开辅助功能模式
  4. 我收到一个页面,告诉我我现在处于辅助功能模式,并且我注意到菜单已更改为其辅助功能版本(不同的组件存在于不同的页面模板中)
  5. 我导航到未访问的页面,它们都处于可访问性模式
  6. 我导航到在打开辅助功能之前访问过的页面,我在non-accessibility版本中看到它们

在最后一步中,我们可以理解,即使在可访问性模式下并且发生资源路径转换(我有日志要证明),页面也会按照默认的不可访问性模式生成。

那么,JSF 中真的有页面缓存吗?如何清除它,这样页面确实会再次呈现?

更新 1

网络监控显示请求确实是发给应用程序的,所以这里没有播放浏览器缓存。

【问题讨论】:

  • 无法访问?真的吗?
  • 您应该尝试确定缓存是发生在客户端还是服务器端。 JSF 缓存 facelets 文件以便不必一次又一次地解析它们,但应该每次都对它们进行评估。另一方面,如果有指示(jsf.project.stage=Production),则仅应在客户端缓存资源(图像、css 等)。您遇到的问题似乎与您的应用程序服务器缓存请求的页面有关。
  • @JasperdeVries:真的,对屏幕作家有好处的东西不一定对可以看到和使用鼠标的用户有好处。你自己试试,你会看到的!
  • @XtremeBiker:好主意,谢谢!网络监控显示请求确实是发给应用程序的,所以这里没有播放浏览器缓存。我刚刚更新了我的问题以添加此详细信息。
  • 带有缓存视图响应的请求是否命中托管 bean 逻辑?

标签: java jsf jsf-2


【解决方案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;
    }
}

结论

我可以满足我所有的要求:

  1. 无需重构 Java 代码,应用程序现在可以在两种不同的模式下运行;
  2. 我只重建了我想要的视图,以防原始视图与屏幕阅读器一起正常工作,它保持原样;
  3. 可随时打开和关闭辅助功能模式,用户可以立即响应此类操作;

我不明白这个解决方案适用于任何类型的应用程序模式,而不仅仅是可访问性。任何时候有人需要根据应用程序或会话参数选择某个视图而不是另一个视图,它都会起作用。例如,文化定制比颜色和语言更进一步,需要完全重新设计视图的多文化应用程序可以利用此模型。

所有这一切的缺点是,当可访问性模式打开并且某个视图存在可访问版本时,JSF 将工作两次,一次构建原始视图,第二次构建可访问性版本。

【讨论】:

  • 为什么要投反对票?与我们分享您的技术意见,让我们知道您更好的想法!
猜你喜欢
  • 2015-02-10
  • 2012-03-11
  • 1970-01-01
  • 2014-06-27
  • 2016-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
相关资源
最近更新 更多