简短的摘要:
项目:一个复合组件,可让我过滤列表并更新外部组件
问题:myfaces 2.1.10 有效,mojarra 2.2.1 无效,但我想使用 jsf 2.2
复合组件filteredList.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:composite="http://java.sun.com/jsf/composite" xmlns:ui="http://java.sun.com/jsf/facelets">
<composite:interface>
<composite:attribute name="label" required="false" default="filter" type="java.lang.String" />
<composite:attribute name="filter" required="true" type="java.lang.String" />
<composite:attribute name="list" required="true" type="java.util.List" />
<composite:attribute name="valueMask" required="false" type="" />
<composite:attribute name="outputId" required="false" type="java.lang.String" />
<composite:attribute name="height" required="false" default="128" type="java.lang.Integer" />
<composite:attribute name="width" required="false" default="128" type="java.lang.Integer" />
<composite:attribute name="destinationId" required="false" type="java.lang.String" />
<composite:attribute name="rowClasses" required="false" type="java.lang.String" />
<composite:attribute name="hoverClass" required="false" default="" type="java.lang.String" />
<composite:attribute name="innerCellClasses" required="false" default="" type="java.lang.String" />
<composite:attribute name="repeatClassOdd" required="false" default="" type="java.lang.String" />
<composite:attribute name="repeatClassEven" required="false" default="" type="java.lang.String" />
</composite:interface>
<composite:implementation>
<h:outputStylesheet name="css/elements.css" />
<h:panelGrid columns="2">
<h:outputText value="#{composite.attrs.label}" />
<h:inputText id="filterfield" value="#{composite.attrs.filter}">
<f:ajax event="keyup" render="table" />
</h:inputText>
</h:panelGrid>
<div id="box#{cc.clientId}" style="overflow: auto">
<h:dataTable id="table" value="#{composite.attrs.list}" var="elem" rowClasses="#{composite.attrs.rowClasses}">
<h:column>
<div id="choose#{elem.id}" style="cursor: pointer" class="#{composite.attrs.hoverClass}">
<h:outputText value="#{elem.html}" rendered="#{elem.renderHtml}" />
<h:dataTable value="#{elem.list}" var="l" rendered="#{elem.renderDatatable}" rowClasses="#{composite.attrs.innerCellClasses}">
<h:column>
<h:outputText value="#{l}" />
</h:column>
</h:dataTable>
<ui:repeat value="#{elem.list}" var="l" varStatus="index" rendered="#{elem.renderRepeat}" columnClasses="#{composite.attrs.innerCellClasses}">
<div class="#{index.odd ? composite.attrs.repeatClassOdd : composite.attrs.repeatClassEven}" style="display: inline">
<h:outputText value="#{l}" />
</div>
</ui:repeat>
</div>
<ui:fragment rendered="#{composite.attrs.destinationId != null}">
<script type="text/javascript">
$(document).ready(function(){
$("[id='choose#{elem.id}']").click(function(){
$("[id='#{composite.attrs.destinationId}']").val("#{elem.id}");
<ui:fragment rendered="#{cc.attrs.valueMask != null}">
$("[id='#{composite.attrs.outputId}']").text("#{composite.attrs.valueMask.getMask(elem.id)}");
</ui:fragment>
$("[id='#{composite.attrs.destinationId}']").trigger('change');
});
});
</script>
</ui:fragment>
</h:column>
</h:dataTable>
</div>
<script type="text/javascript">
$(document).ready(function(){
$("[id='box#{composite.clientId}']").height(#{composite.attrs.height}).width(#{composite.attrs.width});
});
</script>
</composite:implementation>
</ui:composition>
该组件的用法:
<mycomponent:filteredList valueMask="#{testAction.valueMask}" label="label.filter" list="#{testAction.listFilter.list}" filter="#{testAction.listFilter.filter}" height="128" width="384" rowClasses="odd,even" hoverClass="hoverClass" inputfieldClasses="stayontop,stayontop" repeatClassOdd="oddy"
repeatClassEven="eveny" />
使用类TestFilter:
public class TestFilter implements FilterInterface
{
private String filter;
@Override
public List<ListFilterElem> doFilter(List<ListFilterElem> unfilteredList)
{
if (filter == null || filter.trim().equals(""))
{
return unfilteredList;
}
else
{
String[] s = filter.split("\\s+");
Set<ListFilterElem> filtered = new LinkedHashSet<ListFilterElem>();
for (String f : s)
{
for (ListFilterElem e : unfilteredList)
{
if (e.containsIgnoreCase(f))
{
filtered.add(e);
}
}
}
return new ArrayList<ListFilterElem>(filtered);
}
}
@Override
public String getFilter()
{
return filter;
}
@Override
public void setFilter(String filter)
{
this.filter = filter;
}
}
使用的类TestListFilterElem:
public class TestListFilterElem implements ListFilterElem
{
private Integer id;
private List<String> subList;
public TestListFilterElem(Integer id, List<String> subList)
{
this.id = id;
this.subList = subList;
}
@Override
public String getHtml()
{
StringBuilder buf = new StringBuilder();
for (String s : subList)
{
buf.append(s);
buf.append(" ");
}
return buf.toString();
}
@Override
public Integer getId()
{
return id;
}
@Override
public Boolean containsIgnoreCase(String needle)
{
return getHtml().toLowerCase().contains(needle.toLowerCase());
}
@Override
public List<String> getList()
{
return subList;
}
@Override
public Boolean getRenderDatatable()
{
return false;
}
@Override
public Boolean getRenderHtml()
{
return false;
}
@Override
public Boolean getRenderRepeat()
{
return true;
}
}
使用的类 ListFilter:
public class ListFilter
{
private List<ListFilterElem> list;
private FilterInterface filterInterface;
public ListFilter(FilterInterface filterInterface)
{
this.filterInterface = filterInterface;
list = new ArrayList<ListFilterElem>();
}
public void addListElem(ListFilterElem elem)
{
list.add(elem);
}
public void addListElems(List<ListFilterElem> elems)
{
list.addAll(elems);
}
public List<ListFilterElem> getList()
{
return filterInterface.doFilter(list);
}
public void setFilter(String filter)
{
filterInterface.setFilter(filter);
}
public String getFilter()
{
return filterInterface.getFilter();
}
}
使用的类掩码:
public class Mask implements MaskInterface
{
private Integer ensureInteger(Object o) throws Exception
{
if (o == null)
{
return null;
}
else if (o instanceof Integer)
{
return (Integer) o;
}
else if (o instanceof String)
{
return new Integer((String) o);
}
else
{
throw new Exception("cannot convert " + o.getClass() + " to Integer");
}
}
@Override
public String getMask(Object o)
{
try
{
switch (ensureInteger(o))
{
case 1: return "first line";
case 2: return "second line";
case 3: return "third line";
default: return "unknown";
}
}
catch (Exception e)
{
return e.getMessage();
}
}
}
使用的类 ValueMask:
public class ValueMask
{
private MaskInterface maskInterface;
public ValueMask(MaskInterface maskInterface)
{
this.maskInterface = maskInterface;
}
public String getMask(Object o)
{
return maskInterface.getMask(o);
}
}
使用的类TestAction:
@ManagedBean
@SessionScoped
public class TestAction
{
private ListFilter listFilter;
private String variable;
public ListFilter getListFilter()
{
if (listFilter == null)
{
listFilter = new ListFilter(new TestFilter());
listFilter.addListElem(new TestListFilterElem(1, Arrays.asList(new String[]{"this", "is", "the", "first", "list"})));
listFilter.addListElem(new TestListFilterElem(2, Arrays.asList(new String[]{"second", "list"})));
listFilter.addListElem(new TestListFilterElem(3, Arrays.asList(new String[]{"(brackets", "(in", "(brackets)))"})));
}
return listFilter;
}
public ValueMask getValueMask()
{
return new ValueMask(new Mask());
}
}
mojarra 的 Maven 依赖项:
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.1</version>
</dependency>
myfaces 的 Maven 依赖项:
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-api</artifactId>
<version>2.1.10</version>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-impl</artifactId>
<version>2.1.10</version>
</dependency>
Stacktrace 如果使用 mojarra:
SEVERE: Servlet.service() for servlet [FacesServlet] in context with path [/TestJsfComponents] threw exception [null] with root cause
java.lang.NullPointerException
at java.util.Hashtable.put(Hashtable.java:542)
at java.beans.FeatureDescriptor.setValue(FeatureDescriptor.java:194)
at com.sun.faces.facelets.tag.composite.AttributeHandler$CCAttributePropertyDescriptor.getValue(AttributeHandler.java:210)
at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$CompositeComponentMetaRuleset$CompositeMetadataTarget.getPropertyType(CompositeComponentTagHandler.java:464)
at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$CompositeComponentRule.applyRule(CompositeComponentTagHandler.java:540)
at com.sun.faces.facelets.tag.MetaRulesetImpl.finish(MetaRulesetImpl.java:173)
at javax.faces.view.facelets.MetaTagHandler.setAttributes(MetaTagHandler.java:127)
at javax.faces.view.facelets.DelegatingMetaTagHandler.setAttributes(DelegatingMetaTagHandler.java:102)
at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.setAttributes(CompositeComponentTagHandler.java:245)
at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:181)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:190)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:190)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:190)
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:980)
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)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
问题:
如果我使用 myfaces,组件会被渲染并工作(过滤元素正在通过 ajax 即时减少列表),但如果我使用 mojarra(甚至 mojarra 是 2.2.1,myfaces 只是 2.1.10),组件不是由于上面显示的堆栈跟踪而呈现。
为什么会这样?我该怎么做才能让 mojarra 与这个复合组件一起工作?
注释:
这种用法(过滤器类等)的复杂性是由于稍后在另一个复合组件中使用该组件,因此上面提到的接口和实现是必要的。