URL rewriting solution needed for JSF

I was facing the same problem and tried your solution. While it was more or less working, there were still a few glitches. And to be honest, it feels more like fighting the symptoms as opposed to curing the illness.

So here's what finally worked for me:

Instead of setting the deployments apart through the path, I assigned each deployment to its own port:

foo.war <-- http://localhost:8080/ -- | Proxy | <-- http://www.foo.com -- | Client |
bar.war <-- http://localhost:8181/ -- | Proxy | <-- http://www.bar.com -- | Client |

This way, both deployments are able to use / as their context path, hence no need to edit the context path out.

To achieve this, you don't necessarily have to run two application servers. In my case (Wildfly 10.0) it was sufficient to define two undertow servers in the wildfly configuration, each with its own virtual host and http listener, like so:

<server name="foo-server">
   <http-listener name="foo-listener" proxy-address-forwarding="true" socket-binding="foo-http"/>
   <host name="foo-host" default-web-module="foo.war" alias="localhost, foo.com, wwww.foo.com"/>
</server>
<server name="bar-server">
   <http-listener name="bar-listener" proxy-address-forwarding="true" socket-binding="bar-http"/>
   <host name="bar-host" default-web-module="bar.war" alias="localhost, bar.com, wwww.bar.com"/>
</server>

<socket-binding name="foo-http" port="${jboss.http.port:8080}"/>
<socket-binding name="bar-http" port="${jboss.http.port:8181}"/>

You will also need a jboss-web.xml in your project:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
   <server-instance>foo-server</server-instance>
   <virtual-host>foo-host</virtual-host>
   <context-root>/</context-root>
</jboss-web>

The two servers are needed because you can't add a socket binding to a virtual host. So there is a slight overhead here, but negligible compared to running two complete application servers.

Edit 1:

It just occured to me that it is probably not even necessary to use different ports and using one undertow server per deplyoment is probably superfluous as well.

Since the proxy is able to forward the host as requested by the client to the application server, undertow should be able to pick out the proper virtual host via the alias parameter.

So basically, the proxy would relay any request to either foo.com or bar.com to localhost:8080 and let the AS sort things out.

I have not tested this, but here's how it could work (again, this is for Wildfly 10.0):

<server name="default-server">
   <http-listener name="http" proxy-address-forwarding="true" socket-binding="http"/>
   <host name="foo-host" default-web-module="foo.war" alias="foo.com, wwww.foo.com"/>
   <host name="bar-host" default-web-module="bar.war" alias="bar.com, wwww.bar.com"/>
</server>

And the jboss-web.xml would loose the server tag:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
   <virtual-host>foo-host</virtual-host>
   <context-root>/</context-root>
</jboss-web>

In case this works there would be no overhead involved at all.

Edit 2:

Just tested the simplified approach - yep, it works :)


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>

You can use OCPsoft Rewrite URLRewriteFilter for this (not PrettyFaces currently, but you can use them both at the same time until they formally join together after PrettyFaces 4 release - Rewrite is the core project for PrettyFaces 4)

Doing something like this should be fairly straightforward using a single configuration rule. You can obviously fiddle if this rule is either too strict or too general.

.defineRule()
.when(URL.matches("{prefix}" + context.getContextPath() + "{suffix}")
.perform(Substitute.with("{prefix}{suffix}"))

Check out the rewrite site. It's pretty easy to set up. http://ocpsoft.org/rewrite/