The project: a composite component that lets me filter a list and update an outer component
The problem: myfaces 2.1.10 works, mojarra 2.2.1 does not work, but I want to use jsf 2.2
<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" />
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;
}
}
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;
}
}
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();
}
}
}
public class ValueMask
{
private MaskInterface maskInterface;
public ValueMask(MaskInterface maskInterface)
{
this.maskInterface = maskInterface;
}
public String getMask(Object o)
{
return maskInterface.getMask(o);
}
}
@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());
}
}
<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>
<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>
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)
If I use myfaces, the component is rendered and working (filtering elements is reducing the list by ajax on the fly), but if I use mojarra (even mojarra is 2.2.1 and myfaces is just 2.1.10), the component is not rendered due to the stacktrace shown above.
Why is this so and what can I do to make mojarra work with this composite component?
The complexity of this usage (filter classes and such) is due to using this component in another composite component later, therefore the interfaces and implementations mentioned above are necessary.
After a while I found the problem:
<composite:attribute name="valueMask" required="false" type="" />
The attribute type is empty. Seems like myfaces forgives this and mojarra does not. I'd prefer a better stacktrace (message) for such an error.
Nevertheless, removing type solved the problem (in my case I gave type the full qualified class I needed).