将f:ajax添加到compositecomponent时,在AjaxHandler.applyAttachedObject中的NPE

时间:2015-01-09 10:19:11

标签: ajax jsf-2 jsf-2.2 composite-component

我在primefaces 5.1 datatable周围创建了一个复合组件:

没有AJAX的组件(selectorServicio2.xhtml

<ui:component
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:cc="http://java.sun.com/jsf/composite"
  xmlns:c="http://java.sun.com/jsp/jstl/core"
  xmlns:p="http://primefaces.org/ui"
  xmlns:pe="http://primefaces.org/ui/extensions">

  <cc:interface componentType="selectorServicio2">
    <cc:attribute name="servicios" required="true" type="java.util.Collection"
      shortDescription="Colección de servicios entre los que se buscarán los datos"/>
    <cc:attribute name="seleccionSimpleAttr" type="es.imasmallorca.selene.model.prestacion.Servicio"/>
    <cc:attribute name="seleccionMultipleAttr" type="java.util.List"/>
    <cc:attribute name="seleccionMultiple" required="false" type="java.lang.Boolean" default="false"/>
    <cc:attribute name="scrollHeight" required="false" type="java.lang.Integer"/>
  </cc:interface>

  <cc:implementation>
    <p:dataTable id="selectorServicio" widgetVar="#{cc.widgetId}"
      value="#{cc.attrs.servicios}" var="_servicio"
      scrollable="#{not empty cc.attrs.scrollHeight}"   scrollHeight="#{cc.attrs.scrollHeight}"
      selectionMode="#{cc.attrs.seleccionMultiple ? 'multiple' : 'single'}"
      selection="#{cc.attrs.seleccionMultiple ? cc.attrs.seleccionMultipleAttr : cc.attrs.seleccionSimpleAttr}" rowKey="#{_servicio.codigo}">

      <p:column>
        <h:outputText value="#{_servicio.entidad.nombre}"/>
      </p:column>

      <p:column>
        <h:outputText value="#{_servicio.tipoServicio.nombre}"/>
      </p:column>
    </p:dataTable>
  </cc:implementation>
</ui:component>

它有一个支持类(SelectorServicio2.java):

@FacesComponent("selectorServicio2")
public class SelectorServicio2 extends UIInput implements NamingContainer {

  private static final Logger log = Logger.getLogger(SelectorServicio2.class.getName());

  private static final String ID_SERVICIOS_SELECCIONADOS = "ServiciosSeleccionados";

  @Override
  public String getFamily() {
    return UINamingContainer.COMPONENT_FAMILY;
  }

  public void encodeBegin(FacesContext facesContext) throws IOException {
    @SuppressWarnings("unchecked")
    List<Servicio> servicios = (List<Servicio>) this.getAttributes().get("servicios");
    if (servicios == null) {
      servicios = new ArrayList<>();
    }
    Collections.sort(servicios, new ServicioPorNombreEntidadTipoServicio(Criterio.ENTIDAD_TIPOSERVICIO));
    getStateHelper().put("servicios", servicios);
    getStateHelper().put(ID_SERVICIOS_SELECCIONADOS, new ArrayList<Servicio>());

    super.encodeBegin(facesContext);
  }

  @Override
  public Object getValue() {
    ArrayList<Servicio> servicios = new ArrayList<>();
    Servicio servicio = (Servicio) this.getStateHelper().get("servicio");
    if (servicio != null) {
      servicios.add(servicio);
    }
    return servicios;
  }

  public String getWidgetId() {
    return "wdgSelectorServicio2_" + this.getClientId().replace(":", "_");
  }

  public void setServicioSeleccionado(Servicio servicioSeleccionado) {
    if (servicioSeleccionado == null) {
      this.getStateHelper().remove("servicio");
    } else {
      this.getStateHelper().put("servicio", servicioSeleccionado);
    }
  }

  public Servicio getServicioSeleccionado() {
    return (Servicio) this.getStateHelper().get("servicio");
  }
}

这可以按预期工作(text.xhtml):

<!DOCTYPE html>
<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"
  xmlns:p="http://primefaces.org/ui"
  xmlns:imas="http://java.sun.com/jsf/composite/components">

<h:head></h:head>
<h:body>
  <h:form>
    <imas:selectorServicio2 id="serviceSelector"
      servicios="#{testBean.services}"
      seleccionSimpleAttr="#{testBean.selectedService}"
      scrollHeight="110">
    </imas:selectorServicio2>
  </h:form>
</h:body>
</html>

现在,为了添加Ajax支持,我做了:

  1. <cc:clientBehavior name="servicioSeleccionado" event="rowSelect" targets="selectorServicio"/>添加到cc:interface的{​​{1}}。
  2. selectorServicio2.xhtml实现SelectorServicio2(奇怪的是,我不需要实现任何方法,但在编译类时没有任何问题。)
  3. 将ajax标记添加到ClientBehaviorHolder

    test.xhtml
  4. <imas:selectorServicio2 id="serviceSelector" servicios="#{testBean.services}" seleccionSimpleAttr="#{testBean.selectedService}" scrollHeight="110"> <f:ajax event="servicioSeleccionado" listener="#{testBean.selectService}"/> </imas:selectorServicio2> 方法添加到selectService()

    TestBean.java
  5. 在这些更改之后,我得到一个NullPointerException:

      

    java.lang.NullPointerException at   com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyAttachedObject(AjaxHandler.java:333)     在   com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyNested(AjaxHandler.java:258)     在   com.sun.faces.facelets.tag.jsf.core.AjaxHandler.apply(AjaxHandler.java:182)     在   javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)     在   com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:183)     在   com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)     在   javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)     在   javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)     在   com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)     在   javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)     在   javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)     在   com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)     在   javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)     在   javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)     在   com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)     在   com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)     在   com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:161)     在   com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:995)     在   com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:99)     在com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)at   com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)

    我做错了什么?

    我在Java 7上使用带有标准配置(JSF 2.2)的Wildfly 8.1

1 个答案:

答案 0 :(得分:1)

java.lang.NullPointerException
    at com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyAttachedObject(AjaxHandler.java:333)

让我们检查source of AjaxHandler#applyAttachedObject()

332            Collection<String> eventNames = bHolder.getEventNames();
333            if (!eventNames.contains(eventName)) {
334                throw new TagException(this.tag, 
335                    getUnsupportedEventMessage(eventName, eventNames, parent));
336            }

啊哈,getEventNames()返回null。该方法已在UIComponentBase上实施,其javadoc说明如下:

  

这是ClientBehaviorHolder.getEventNames()的默认实现。 UIComponent未实现ClientBehaviorHolder接口,但为ClientBehaviorHolder定义的方法提供了默认实现,以简化子类实现。希望支持ClientBehaviorHolder契约的子类必须声明子类实现ClientBehaviorHolder,并且必须覆盖此方法以返回客户端事件名称的非空Collection组件支持。

从技术上讲,您应该在组件中覆盖getEventNames(),返回一组受支持的事件名称。

然而,你实际上并不需要它。 <cc:clientBehavior>已经在嵌套在复合中的<p:dataTable>组件上重新定位它,该组件已经正确实现了该接口和方法。因此,您应该从支持组件中删除 ClientBehaviorHolder接口。

此外,<f:ajax>不适用于PrimeFaces特定事件,您应该使用<p:ajax>。它将呈现一个脚本,该脚本又调用PrimeFaces特定的ajax客户端API。