添加 f:ajax 到 compositecomponent 时 AjaxHandler.applyAttachedObject 中的 NPE
NPE in AjaxHandler.applyAttachedObject when adding f:ajax to compositecomponent
我围绕 primefaces 5.1 数据表创建了一个复合组件:
没有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>
有后盾class (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 支持,我这样做:
- 将
<cc:clientBehavior name="servicioSeleccionado" event="rowSelect" targets="selectorServicio"/>
添加到 selectorServicio2.xhtml
的 cc:interface
。
- 使
SelectorServicio2
实现 ClientBehaviorHolder
(奇怪的是,我不需要实现任何方法,但编译 class 没有问题)。
将ajax标签添加到test.xhtml
:
<imas:selectorServicio2 id="serviceSelector"
servicios="#{testBean.services}"
seleccionSimpleAttr="#{testBean.selectedService}"
scrollHeight="110">
<f:ajax event="servicioSeleccionado" listener="#{testBean.selectService}"/>
</imas:selectorServicio2>
将selectService()
方法添加到TestBean.java
:
public void selectService() {
log.warning("SERVICIO SELECCIONADO !!!!");
}
在这些更改之后,我得到一个 NullPointerException:
java.lang.NullPointerException at
com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyAttachedObject(AjaxHandler.java:333)
at
com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyNested(AjaxHandler.java:258)
at
com.sun.faces.facelets.tag.jsf.core.AjaxHandler.apply(AjaxHandler.java:182)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:183)
at
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at
com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
at
com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)
at
com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:161)
at
com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:995)
at
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:99)
at 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
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 表示如下:
This is a default implementation of ClientBehaviorHolder.getEventNames()
. UIComponent
does not implement the ClientBehaviorHolder
interface, but provides default implementations for the methods defined by ClientBehaviorHolder
to simplify subclass implementations. Subclasses that wish to support the ClientBehaviorHolder
contract must declare that the subclass implements ClientBehaviorHolder
, and must override this method to return a non-Empty Collection
of the client event names that the component supports.
从技术上讲,您应该在组件中重写 getEventNames()
,返回支持的事件名称的集合。
然而,你实际上并不需要它。 <cc:clientBehavior>
已经将其重定向到嵌套在复合材料中的 <p:dataTable>
组件上,该组件已经正确实现了该接口和方法。因此,您应该 删除 支持组件中的 ClientBehaviorHolder
接口。
此外,<f:ajax>
不适用于 PrimeFaces 特定事件,您应该改用 <p:ajax>
。它将呈现一个脚本,该脚本依次调用 PrimeFaces 特定的 ajax 客户端 API.
我围绕 primefaces 5.1 数据表创建了一个复合组件:
没有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>
有后盾class (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 支持,我这样做:
- 将
<cc:clientBehavior name="servicioSeleccionado" event="rowSelect" targets="selectorServicio"/>
添加到selectorServicio2.xhtml
的cc:interface
。 - 使
SelectorServicio2
实现ClientBehaviorHolder
(奇怪的是,我不需要实现任何方法,但编译 class 没有问题)。 将ajax标签添加到
test.xhtml
:<imas:selectorServicio2 id="serviceSelector" servicios="#{testBean.services}" seleccionSimpleAttr="#{testBean.selectedService}" scrollHeight="110"> <f:ajax event="servicioSeleccionado" listener="#{testBean.selectService}"/> </imas:selectorServicio2>
将
selectService()
方法添加到TestBean.java
:public void selectService() { log.warning("SERVICIO SELECCIONADO !!!!"); }
在这些更改之后,我得到一个 NullPointerException:
java.lang.NullPointerException at com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyAttachedObject(AjaxHandler.java:333) at com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyNested(AjaxHandler.java:258) at com.sun.faces.facelets.tag.jsf.core.AjaxHandler.apply(AjaxHandler.java:182) at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137) at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:183) at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203) at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120) at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137) at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203) at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120) at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137) at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203) at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120) at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95) at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93) at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87) at com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:161) at com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:995) at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:99) at 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.1java.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 表示如下:
This is a default implementation of
ClientBehaviorHolder.getEventNames()
.UIComponent
does not implement theClientBehaviorHolder
interface, but provides default implementations for the methods defined byClientBehaviorHolder
to simplify subclass implementations. Subclasses that wish to support theClientBehaviorHolder
contract must declare that the subclass implementsClientBehaviorHolder
, and must override this method to return a non-EmptyCollection
of the client event names that the component supports.
从技术上讲,您应该在组件中重写 getEventNames()
,返回支持的事件名称的集合。
然而,你实际上并不需要它。 <cc:clientBehavior>
已经将其重定向到嵌套在复合材料中的 <p:dataTable>
组件上,该组件已经正确实现了该接口和方法。因此,您应该 删除 支持组件中的 ClientBehaviorHolder
接口。
此外,<f:ajax>
不适用于 PrimeFaces 特定事件,您应该改用 <p:ajax>
。它将呈现一个脚本,该脚本依次调用 PrimeFaces 特定的 ajax 客户端 API.