Thursday, August 17, 2017

Extensionless URLs with JSF 2.3

An extensionless URL is a URL without a final suffix like .xhtml, .html, .jsp, etc. Such a suffix is seen as technical "clutter" that's hard to remember for humans. Servers often need it though to route a request to the right controller.

JSF, a Java EE MVC framework, has supported extensionless URLs for some time via PrettyFaces (now merged to the general Rewrite framework) and OmniFaces. Both of these solutions used various workarounds to trick JSF into working with extensionless URLs.

Though JSF 2.3 does, unfortunately, still not support extensionless URLs fully out of the box via e.g. a single parameter, it can provide support for it by basically combining the new support for exact mapping and the API for obtaining a list of all view resources. Additionally combining this with the Servlet 3.1 feature for dynamically adding Servlet mappings and some JDK8 streaming and lambdas, makes it possible to enable extensionless support with just 2 statements (albeit somewhat long statements):

@WebListener
public class MappingInit implements ServletContextListener {
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        FacesContext context = FacesContext.getCurrentInstance();
        
        sce.getServletContext()
           .getServletRegistrations()
           .values()
           .stream()
           .filter(e -> e.getClassName().equals(FacesServlet.class.getName()))
           .findAny()
           .ifPresent(
               reg -> context.getApplication()
                             .getViewHandler()
                             .getViews(context, "/", RETURN_AS_MINIMAL_IMPLICIT_OUTCOME)
                             .forEach(e -> reg.addMapping(e)));
    }
}

What the above code does is finding the existing FacesServlet, then getting all views for the entire application in a form that happens to be exactly suitable for extensionless URLs, and then adding each of them as mapping to the FacesServlet we previously found.

After adding the above shown WebListener to an application, its view can be requested via URLs like example.com/login, example.com/users/all etc.

The example was tested on Payara Server 5, of which a snapshot can be downloaded from the snapshot repository. An initial alpha will be released very soon, but in the mean time the latest version can be downloaded here:
payara-5.0.0.173-SNAPSHOT.zip.

Arjan Tijms

2 comments: