如何在 "namespaced mode" 中使用 jsf

How to use jsf in "namespaced mode"

在我们想要集成 jsf-applications 提供的一些片段的网站中,想想 dashboard-app 或 "portal-light"。在分析需求时,我们看到了 Arjan Tjims 在 jsf 2.3 new features 上的博客 post,他在博客中提到了一个新的 "namespaced mode":

In namespaced mode, which is specifically intended for Portlets but can be used in other environments as well, the partial response is given an id that's taken to be the "naming container id". All predefined postback parameter names (such as "javax.faces.ViewState", "javax.faces.ClientWindow", "javax.faces.RenderKitId", etc) are prefixed with this and the naming separator (default ":"). e.g. javax.faces.ViewState" becomes "myname:javax.faces.ViewState". Namespaced mode is activated when the UIViewRoot instance implements the NamingContainer interface.

我们的应用程序可能是 "namespaced mode" 的一个用例,所以我们想试一试。

我们构建了一个 MyUIViewRoot,其中我们实现了 NamingContainer 并包装了原始的 UIViewRoot-实例。我们在 faces-config.xml 中注册了一个 MyViewHandler 来处理 ViewRoot 的包装。为了进行测试,我们使用了一个带有两个 <h:form> 元素(似乎很重要)的简单计数器应用程序。

我们发现"namespace mode"似乎被激活了,例如javax.faces.ViewState确实被一些命名空间前置,变成了j_id1:javax.faces.ViewState:0。但是这两个操作都不再有效 - postback 请求不再恢复视图而是创建一个新视图。因此,通过我们简单的方法,我们遗漏了一些东西(顺便说一句,仅从 MyUIViewRoot 中删除 implements NamingContainer,反应用程序再次正常工作)。

该应用程序 运行 在 payara-5 应用程序服务器中。

我们的index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head/>
<h:body>
  <h:form id="counterForm">
    <h:panelGrid columns="2">
      <h:outputLabel value="Counter" />
      <h:outputText value="#{counterUiController.counter}" />
    </h:panelGrid>
    <h:commandButton value="inc" action="#{counterUiController.incAction}">
      <f:ajax execute="@form" render="@form" />
    </h:commandButton>
  </h:form>
  <h:form id="resetForm">
    <h:commandButton value="reset" action="#{counterUiController.resetAction}">
      <f:ajax execute="@form" render=":counterForm" />
    </h:commandButton>
  </h:form>
</h:body>
</html>

CounterUiController:

@Named
@ViewScoped
public class CounterUiController implements Serializable {

    private int counter;

    public int getCounter() {
        return counter;
    }

    public void incAction() {
        counter++;
    }

    public void resetAction() {
        counter=0;
    }
}

我们的 UIViewRoot-实施:

public class MyUIViewRoot extends UIViewRoot implements NamingContainer, FacesWrapper<UIViewRoot> {

    private static final Logger LOG = Logger.getLogger(MyUIViewRoot.class.getName());

    private UIViewRoot wrapped;

    public MyUIViewRoot(UIViewRoot wrapped) {
        this.wrapped = wrapped;
        LOG.log(Level.INFO, "new instance created: {0}", this);
    }

    @Override
    public UIViewRoot getWrapped() {
        return wrapped;
    }

    @Override
    public String createUniqueId() {
        return wrapped==null ? null : wrapped.createUniqueId();
    }

    @Override
    public void setId(String id) {
        if( wrapped!=null ) {
            wrapped.setId(id);
        }
    }
    // all other methodes delegated to `wrapped` directly
}

我们的ViewHandler:

public class MyViewHandler extends ViewHandlerWrapper {

    private static final Logger LOG = Logger.getLogger(MyViewHandler.class.getName());

    public MyViewHandler(ViewHandler wrapped) {
        super(wrapped);
    }

    @Override
    public UIViewRoot createView(FacesContext context, String viewId) {
        UIViewRoot retval = super.createView(context, viewId);
        retval = wrapIfNeeded(retval);
        LOG.log(Level.INFO, "view created: {0}", retval);
        return retval;
    }

    @Override
    public UIViewRoot restoreView(FacesContext context, String viewId) {
        UIViewRoot retval =  super.restoreView(context, viewId);
        retval = wrapIfNeeded(retval);
        LOG.log(Level.INFO, "view restored: {0}", retval);
        return retval;
    }

    private UIViewRoot wrapIfNeeded(UIViewRoot root) {
        if (root != null && !(root instanceof MyUIViewRoot)) {
            LOG.log(Level.INFO, "view wrapped: {0}, {1}", new Object[] { root, root.getId() });
            return new MyUIViewRoot(root);
        } else {
            return root;
        }
    }
}

你需要更换 UIViewRoot 不要换行。

public class NamespacedView extends UIViewRoot implements NamingContainer {
    //
}

然后在faces-config.xml.

<component>
    <component-type>javax.faces.ViewRoot</component-type>
    <component-class>com.example.NamespacedView</component-class>
</component>

基本上就这些了。另见 the Mojarra IT on this