5

I am trying to implement a "generic" view where (part of) the content displayed depends on the URL. E.g.

If /somepath/somepage.xhtml points to a non existing file, instead of going straight to a 404 error I want to try to retrieve /somepath/somepage.xhtml's content from the database using a generic view, /genericview.xhtml, where I have something like:

<h:outputText value="#{genericViewBean.content_lg}"
                escape="false" />

which, if found by the backing bean, would output the content of the database entry from a tgenericcontent table, depending on the originally requested viewId:

 webpath                              | content
 /somepath/somepage.xhtml             | <p>This is a test</p>
 /someotherpath/someotherpage.xhtml   | <p>A different test</p>

If the view content is not found in that table then the standard 404 error would be returned.

The closest I got wast to clone /genericview.xhtml changing only the file path (for example, to /somepath/somepage.xhtml). But that gets me one exact copy of the file per view, it is quite messy, and it doesn't allow me to create a new url just by adding an entry to my database.

How can I get the same result without cloning /genericview.xhtml?

(P.S: I have read about prettyfaces, but isn't there a simpler solution?)

NotGaeL
  • 8,344
  • 5
  • 40
  • 70
  • 1
    So... a front controller? Not unlike what `FacesServlet` is already doing? Prettyfaces or writing your own front controller (which must correspond to either an existing page or servlet) are pretty much your only options. Within your own front controller, you now have flexibility to dispatch the request to the appropriate view using a variety of mechanisms – kolossus Jan 13 '15 at 05:02
  • given the options, it's probably best if I deploy prettyfaces and set a faces-config redirecting a /dynamic/* path to /genericview.xhtml?path=* or something like that... – NotGaeL Jan 13 '15 at 06:20

1 Answers1

9

For that, normally a servlet filter is being used. PrettyFaces, UrlRewriteFilter and FacesViews also do it that way.

You can get the request URI by HttpServletRequest#getRequestURI(). You can check the existence of a web resource by ServletContext#getResource() which will return null on non-existent resources. If the resource exists, just continue the request by FilterChain#doFilter(), else forward the request to the generic view by RequestDispatcher#forward().

All in all, this is how the filter could look like:

@WebFilter("/*")
public class GenericViewFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        String relativeRequestURI = request.getRequestURI().substring(request.getContextPath().length());

        boolean resourceExists = request.getServletContext().getResource(relativeRequestURI) != null;
        boolean facesResourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER));

        if (resourceExists || facesResourceRequest) {
            chain.doFilter(request, response);
        }
        else {
            request.getRequestDispatcher("/genericview.xhtml").forward(request, response);
        }
    }

    // ...
}

In the /genericview.xhtml, the original request URI is available as request attribute keyed with RequestDispatcher#FORWARD_REQUEST_URI. You could use it in @PostConstruct of backing bean associated with the view in order to pull the right content from the DB.

String originalRequestURI = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
// ...
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Although I was already done implementing a super simple Rewrite (http://www.ocpsoft.org/rewrite/) configuration provider class (The prettyfaces guys have definitely come up with a great successor to prettyfaces: only one extra dependency on my pom and implementing the configuration provider class in my project to have it all ready without any additional changes required), your answer describes exactly what I was looking for. Thank you very much! – NotGaeL Jan 13 '15 at 21:43