tamuraです。

ローカルで開発しているときは問題なかったのに、 本番環境は reverse proxy を使っているので、 JSFが生成するURLが違ってしまう場合がある、ということがあります。

/xxxx/app に来たアクセスを Tomcat の /app に横流しする

<Location /xxxx/app/>
  ProxyPass ajp://localhost:8009/app/
  ProxyPassReverse ajp://localhost:8009/app/
  ProxyPassReverseCookiePath /app/ /xxxx/app/
</Location>

このとき、このようなJSFがあった場合

JSFに書かれた内容

<h:form>
  <ul>
    <li>
      <h:commandLink action="#{test.init}" ajax="false" immediate="true">
        test
      </h:commandLink>
  </ul>
</h:form>

通常であればこのようなHTMLが生成されます。

<form id="xxxx" nam="xxxx" method="post" action="/app/index.xhtml" enctype="application/x-www-form-urlencoded">
  <!-- 省略 -->
  <ul>
    <li>
      <script type="text/javascript" src="/app/ajax.faces.resource/jsf.js.xhtml?lnjavax.faces"></script>
      <a href="#" onclick="xxxxxxxx">
        test
      </a>
    </li>
  </ul>
</form>

でも実際に生成してほしいHTMLはこのような形になります。

<form id="xxxx" nam="xxxx" method="post" action="/operation/app/index.xhtml" enctype="application/x-www-form-urlencoded">
  <!-- 省略 -->
  <ul>
    <li>
      <script type="text/javascript" src="/operation/app/ajax.faces.resource/jsf.js.xhtml?lnjavax.faces"></script>
      <a href="#" onclick="xxxxxxxx">
        test
      </a>
    </li>
  </ul>
</form>

ViewHandler で対応する

ViweHandlerを使って、URLを生成しているタイミングで proxy 用のパスを追加します。

(ViewHandlerすべてを実装するのは面倒なので、ViewHandlerWrapperを使っています)

public class ReverseProxyViewHandler extends ViewHandlerWrapper {

    protected ViewHandler defaultHandler;

    public ReverseProxyViewHandler(ViewHandler defaultHandler) {
        super();
        this.defaultHandler = defaultHandler;
    }

    @Override
    public ViewHandler getWrapped() {
        return this.defaultHandler;
    }

    @Override
    public String getActionURL(FacesContext context, String viewId) {
        String actionURL = defaultHandler.getActionURL(context, viewId);
        String proxyPath = getProxyPath(context);
        return genURL(proxyPath, actionURL);
    }

    @Override
    public String getResourceURL(FacesContext context, String viewId) {
        String resourceURL = defaultHandler.getResourceURL(context, viewId);
        String proxyPath = getProxyPath(context);
        return genURL(proxyPath, resourceURL);
    }

    private String getProxyPath(FacesContext context) {
        String proxyPath = context.getExternalContext().getInitParameter("PROXY_PATH");
        if (proxyPath == null) {
            return "";
        }
        else {
            return proxyPath;
        }
    }

    private String genURL(String proxyPath, String url) {
        StringBuilder buf = new StringBuilder(proxyPath).append(url);
        return buf.toString();
    }
}

#getProxyPath(FacesContext)
web.xml
に設定した初期値を取得するようにしてます。

設定

web.xml に proxy 用のパスを設定します。

  <!-- for Reverse Proxy -->
  <context-param>
      <param-name>PROXY_PATH</param-name>
      <param-value>/xxxx</param-value>
  </context-param>

faces-config.xml に ViewHandler を指定します。

    <!-- for Reverse Proxy -->
    <application>
        <view-handler>com.github.tamurashingo.jsfapp.ReverseProxyViewHandler</view-handler>
    </application>

この内容で view を生成すると、 action や img のパスが proxy を考慮した形になります。