Как работать с режимами приложения в JSF

Мое приложение (JSF 2, Java 6, JBoss 7.1) должно предлагать два режима работы: режим доступности и не доступность.

В режиме доступности некоторые (не все) страницы имеют особый дизайн, который лучше читать с помощью программы чтения с экрана. Разница между двумя режимами является чисто визуальной, управляемые beans точно такие же. В идеале, код Java не должен быть изменен.

Большая часть работы выполнена:

  • Для переключения между режимами была добавлена ​​ссылка на верх страницы.
  • Была добавлена ​​поддержка bean для обращения к клику по ссылке
  • Когда доступность включена, атрибут добавляется в файл cookie сеанса, чтобы отметить его доступность.
  • Добавлен ResourceResolver для перезаписи пути страниц к доступной версии, когда доступность включена для определенного запроса от определенного пользователя.

Со всем этим он работает почти отлично, но похоже, что есть какой-то вид кеша, который ломает мое решение. Рассмотрим следующий сценарий:

  • Приложение запускается в режиме отсутствия доступа
  • Я перемещаюсь по нескольким страницам
  • Включить режим доступности, нажав соответствующую ссылку
  • Я получаю страницу с сообщением о том, что я сейчас в режиме доступности, и я заметил, что меню изменилось на его версию доступности (другой компонент, существующий в другом шаблоне страницы).
  • Я перехожу к страницам, которые не были посещены, и все они находятся в режиме доступности.
  • Я перехожу к посещаемым страницам, прежде чем переключать доступ к включенным, и я вижу их в не доступной версии

На последнем шаге мы можем понять, что даже в режиме доступности и с использованием перевода пути ресурса (у меня есть журналы для подтверждения), страницы генерируются так, как они были в режиме по умолчанию, а не в доступе.

Итак, действительно ли есть кеш страниц в JSF? Как я могу его очистить, так что страницы будут снова отображены?

Обновление 1

Мониторинг сети показал, что запрос действительно выдается в приложение, поэтому здесь не используется кеш браузера.

Ответ 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 будет работать дважды, один раз, чтобы создать исходное представление, и второй раз, чтобы построить доступный версия.