javajsfweb-applicationsjsf-2prettyfaces

URL rewriting solution needed for JSF


Suppose the following application landscape:

+-----------------+
| App server      |
+-----------------+
|                 |                                   +-------+
| ear1            |                                   |       |
|  +-web1 (/ctx1) +--<-- http://localhost/ctx1/xxx/ --+       +--<-- http://www.example.com/xxx/
|                 |                                   |       |
|                 |                                   | proxy |
| ear2            |                                   |       |
|  +-web2 (/ctx2) +--<-- http://localhost/ctx2/yyy/ --+       +--<-- http://abc.example.com/yyy/
|                 |                                   |       |
+-----------------+                                   +-------+

As you can see, proxy (nginx in my case) is forwarding requests to to a single application server instance, which in turn has multiple web modules with different context paths. Of course I dont want my public server to expose internal context roots and proxy does it's job well, wraps and unwraps http requests, etc. But there is still one big problem: JSF-generated html code (links, css, js resources, form actions) contains context paths, /ctx1 and /ctx2 in my case. That's what I want to avoid.

I nave no solution at this moment of time except of using more and more different instances (domains) of application server, causing my hardware resources to fade away. As i understand it, I need to extend my JSF applications with some wrappers, potentially registered in faces-config.xml, which would remove context prefix in generated html. Any other solutions are also welcome.

Please point me in the right direction.


Solution

  • I'm posting solution which may be helpful for others facing the same problem. All I needed to do is implementing my own javax.faces.application.ViewHandler and register it in faces-config.xml :

    public class CustomViewHandler extends ViewHandlerWrapper {
      private ViewHandler wrappped;
    
      public CustomViewHandler(ViewHandler wrappped) {
        super();
        this.wrappped = wrappped;
      }
    
      @Override
      public ViewHandler getWrapped() {
        return wrappped;
      }
    
      @Override
      public String getActionURL(FacesContext context, String viewId) {
        String url =  super.getActionURL(context, viewId);
        return removeContextPath(context, url);
      }
    
      @Override
      public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters, boolean includeViewParams) {
        String url =  super.getRedirectURL(context, viewId, parameters, includeViewParams);
        return removeContextPath(context, url);
      }
    
      @Override
      public String getResourceURL(FacesContext context, String path) {
        String url = super.getResourceURL(context, path);
        return removeContextPath(context, url);
      }
    
      private String removeContextPath(FacesContext context, String url) {
        ServletContext servletContext = (ServletContext) context.getExternalContext().getContext();
        String contextPath = servletContext.getContextPath();
        if("".equals(contextPath)) return url; // root context path, nothing to remove
        return url.startsWith(contextPath) ? url.substring(contextPath.length()) : url;
      }
    }
    

    faces-config.xml :

    <faces-config xmlns="http://java.sun.com/xml/ns/javaee"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
                  version="2.0">
      <application>
        <view-handler>test.CustomViewHandler</view-handler>
      </application>
    </faces-config>