JSF所需的URL重写解决方案

时间:2012-03-28 10:50:02

标签: java jsf web-applications jsf-2 prettyfaces

假设以下应用程序格局:

+-----------------+
| 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/
|                 |                                   |       |
+-----------------+                                   +-------+

正如您所看到的,代理(在我的情况下为nginx)是将请求转发到单个应用程序服务器实例,而后者又有多个具有不同上下文路径的Web模块。当然我不希望我的公共服务器暴露内部上下文根和代理它的工作做得好,包装和解包http请求等。但仍然有一个大问题:JSF生成的HTML代码(链接,css,js资源,表单在我的案例中,action)包含上下文路径/ctx1/ctx2。这就是我想要避免的。

除了使用越来越多不同的应用服务器实例(域)之外,我现在没有解决方案,导致我的硬件资源逐渐消失。据我所知,我需要使用一些包装器来扩展我的JSF应用程序,这些包装器可能在faces-config.xml中注册,这将在生成的html中删除上下文前缀。还欢迎任何其他解决方案。

请指出正确的方向。

3 个答案:

答案 0 :(得分:5)

你可以使用OCPsoft Rewrite URLRewriteFilter(目前不是PrettyFaces,但你可以同时使用它们,直到他们在PrettyFaces 4发布后正式加入一起 - Rewrite是PrettyFaces 4的核心项目)

使用单个配置规则执行此类操作应该相当简单。如果这条规则过于严格或过于笼统,你显然可以摆弄。

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

查看重写网站。这很容易设置。 http://ocpsoft.org/rewrite/

答案 1 :(得分:4)

我发布的解决方案可能对面临同样问题的其他人有所帮助。我需要做的只是实现我自己的javax.faces.application.ViewHandler并在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>

答案 2 :(得分:1)

我遇到了同样的问题并尝试了你的解决方案。虽然它或多或少有效,但仍然存在一些问题。说实话,感觉更像是对抗症状而不是治愈疾病。

所以这就是最终对我有用的东西:

我没有通过路径将部署分开,而是将每个部署分配到自己的端口:

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

这样,两个部署都可以使用/作为其上下文路径,因此无需编辑上下文路径。

要实现此目的,您不一定要运行两个应用程序服务器。在我的案例中(Wildfly 10.0),在wildfly配置中定义两个underow服务器就足够了,每个服务器都有自己的虚拟主机和http监听器,如下所示:

<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}"/>

您的项目中还需要一个jboss-web.xml:

<?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>

需要两台服务器,因为您无法将套接字绑定添加到虚拟主机。因此这里有一点点开销,但与运行两个完整的应用服务器相比可忽略不计。

修改1:

我刚刚发现它可能甚至不需要使用不同的端口,并且每个deplyoment使用一个底部服务器也可能是多余的。

由于代理能够根据客户端的请求将主机转发给应用服务器,因此应该能够通过别名参数选择合适的虚拟主机。

所以基本上,代理会将任何请求转发到foo.com或bar.com到localhost:8080并让AS解决问题。

我没有测试过这个,但是这里的工作原理(再次,这是针对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>

jboss-web.xml会丢失服务器标签:

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

如果这种情况有效,根本不会有任何开销。

编辑2:

刚刚测试了简化方法 - 是的,它有效:)