3

A certain managed bean has an action method that returns "/private/myview.jsf". However, without changing this code, I want to perform some checks and eventually render the view "/private/other/myview.jsf".

So, in summary, I want to translate "/private/myview.jsf" to "/private/other/myview.jsf" somewhere after the method return and before the actual view rendering.

How to achieve that?


Environment:

  1. Eclipse Luna
  2. Java 1.7
  3. JSF 2

Current condition

Web application fully operational but not developed to support accessibility features.

Requirement for the next version

To be an application good enough to be used by blind people who uses screen readers.

Details

After some study, we reached the conclusion that we will have to have an accessible version of each page we have nowadays.

We will design such accessible version of each page, the matter is when to show the not accessible version and when to show the accessible version.

We decided that the application will turn to accessible mode (that will not be the default state) when the user clicks a certain link in the top of the page. In accessible mode, only accessible versions of pages are rendered.

We don't want to review all the application, what we want is to intercept some phase of JSF and replace the outcome that should be rendered. For instance, consider the follow:

  1. A certain page has a link or a button to another one, let's say "mysettings.xhtml";
  2. When in accessible mode, we would like to tell to JSF to not render "mysettings.xhtml", instead "mysettings_ac.xhtml" or "accessible/mysettings.xhtml" should be rendered;
  3. Both pages will interact with the very same managed beans and will provide the very same features, but one will be good to the ones who can see and the other will be designed to be comfortable to screen reader users.

Thanks in advance!

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
AlexSC
  • 1,823
  • 3
  • 28
  • 54
  • why can't you use the 'new accessible pages' for non disabled people to? – Kukeltje Jun 09 '16 at 18:01
  • @Kukeltje: because what makes a page good to a blind user makes it not good to ones who can see. – AlexSC Jun 10 '16 at 10:29
  • @BalusC: What about an action in a managed bean that nowadays returns `"myseetings.xhtml"` and should, in accessible mode, return `"mysettings_ac.xhtml"`? My problem is not restricted to page-to-page navigation. In a JSF application it's very usual to respond to an event by running a domain rule and navigating to some other page (like saving a record and going back to the previous page). – AlexSC Jun 10 '16 at 10:33
  • @AlexSC: If I remember all 'standards' in this area correctly, you should (without to much hassle) be able to use 1 page when using jsf passthrough attributes. PrimeFaces has lots of this already build in. So if you have specific problems where this fails or should, I'm curious. – Kukeltje Jun 10 '16 at 10:43
  • @Kukeltje: in some of our pages we have panels that only show their contents when the user clicks a certain button. Our users prefer like that, since the content of such a panel is needed only in rare situations. For a blind person with a screen reader we will handle this by navigating to a different page. – AlexSC Jun 10 '16 at 14:23
  • Why not conditionally render it? – Kukeltje Jun 11 '16 at 10:54
  • @Kukeltje: because we don't want to change all our views and make them more complex they already are. We understand that to different audiences we should have different views. It's like designing a view to be shown to chinese people. It will be organized differently because the language is deeply different. – AlexSC Jun 13 '16 at 10:25
  • ok... remarkable... never heard nor experienced things like this. Good luck anyway. Great accessibility gets this attention – Kukeltje Jun 13 '16 at 22:43
  • Your question is too broad to give a focused answer, but a custom resource handler/resolver should be able to handle this. Essentially something like this: http://stackoverflow.com/questions/13292272/obtaining-facelets-templates-files-from-an-external-filesystem-or-database You just do some checks in there and then alter the returned resource (while URL stays the same). Does that meet your requirements? – BalusC Aug 31 '16 at 11:53
  • Thanks for you comment, I'll check. Since you considered the text too broad, I added some details to make it more clear. – AlexSC Aug 31 '16 at 12:07
  • @BalusC: I gave a try on your suggestion. Breakpoints show me that the `ResourceHandlerWrapper` is being created at the application startup and the method `getWrapped()` is called many times during the runtime, but the method `createViewResource()` remains uncalled. Any idead why? It seemed to me that this would be indeed the way, but something is missing... – AlexSC Aug 31 '16 at 13:21
  • Apparently webapp doesn't actually use JSF 2.2 during runtime. Head to JSF 2.0 approach in bottom. – BalusC Aug 31 '16 at 13:48
  • @BalusC: done already! In the bull's eye, my friend. It worked beautifully. Please, post your suggestion as an answer so I can tick it! – AlexSC Aug 31 '16 at 14:08

2 Answers2

4

So, in summary, I want to translate "/private/myview.jsf" to "/private/other/myview.jsf" somewhere after the method return and before the actual view rendering.

If you're already on JSF 2.2, you can do this by providing a custom ResourceHandeler wherein you return the desired view resource in createViewResource() based on some condition.

public class YourResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public YourResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ViewResource createViewResource(FacesContext context, final String name) {
        if (someCondition) {
            name = name.replace("/private/", "/private/other/");
        }

        return super.createViewResource(context, name);
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}

Which is registered in faces-config.xml as below:

<application>
    <resource-handler>com.example.YourResourceHandler</resource-handler>
</application>

Or if you're not on JSF 2.2 yet, then use ResourceResolver instead.

public class YourResourceResolver extends ResourceResolver {

    private ResourceResolver parent;

    public YourResourceResolver(ResourceResolver parent) {
        this.parent = parent;
    }

    @Override
    public URL resolveUrl(String path) {
        if (someCondition) {
            path = path.replace("/private/", "/private/other/");
        }

        return parent.resolveUrl(path);
    }

}

Which is registered in web.xml as below:

<context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>com.example.YourResourceResolver</param-value>
</context-param>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
-1

As I can see, there can be two probable solutions to it.

  1. using redirect() method

    returnAction(){ ExternalContext context = FacesContext.getCurrentInstance().getExternalContext(); context.redirect("/private/other/myview.jsf"); //Relative path might vary based upon your business logic }

  2. conditional redirect after action return; we can maintain a navigation rule in faces-config.xml something like below:

    <navigation-rule>
        <navigation-case>
            <from-action>#{yourManagedBean.actionMethod}</from-action>
            <if>#{yourManagedBean.redirectAccessibleFlag}</if>   <!-- We maintain a boolean variable redirectAccessibleFlag in the managed Bean and set its value to true or false conditionally-->
            <to-view-id>/private/other/myview.jsf</to-view-id>
           <redirect/>
        </navigation-case>
    </navigation-rule>

In the second alternative, you can put up the managed bean at ApplicationScope in order to be utilized across multiple JSF pages.

Although these solutions are from the general concepts, they might not necessarily fall in the "somewhere after the method return and before the actual view rendering." But you can check if it solves the purpose.

Akash Mishra
  • 682
  • 1
  • 5
  • 13