Pregunta

Short summary:

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

The composite component 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>

Usage of this component:

<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" />

Used class 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;
  }
}

Used class 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;
  }
}

Used class 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();
  }
}

Used class Mask:

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();
    }
  }
}

Used class ValueMask:

public class ValueMask
{
  private MaskInterface maskInterface;

  public ValueMask(MaskInterface maskInterface)
  {
    this.maskInterface = maskInterface;
  }

  public String getMask(Object o)
  {
    return maskInterface.getMask(o);
  }
}

Used class 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());
  }
}

Maven dependencies for mojarra:

<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>

Maven dependencies for myfaces:

<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>

The Stacktrace if using 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)

The Question:

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?

Annotations:

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.

¿Fue útil?

Solución

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).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top