3. Exploiting Derived Inverse Reference Properties in the User Interface

We can now exploit the derived inverse reference properties Publisher::publishedBooks and Author::authoredBooks for more efficiently creating a list of associated books in the retrieve/list all publishers and retrieve/list all authors use cases.

3.1. Show information about published books in Retrieve/List All

For showing information about published books in the retrieve/list all publishers use case, we can now exploit the derived inverse reference property publishedBooks:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:dataTable value="#{publisherController.publishers}" var="p">
      ...
      <h:column>
        <f:facet name="header">Published books</f:facet>
        <h:outputText value="#{p.publishedBooks}" escape="false" 
          converter="pl.m.converter.BookListConverter"/>
      </h:column>
    </h:dataTable>
    <h:button value="Back" outcome="index" />
  </ui:define>
</ui:composition>

In the case of unidirectional associations, for the case of the retrieve/list all books use case, we have used a method in the Book model class, i.e., getAuthorNames(), which returns a comma-separated list of author names as a string:

public class Book {
  ...
  public String getAuthorNames() {
    String result = "";
    int i = 0, n = 0;
    if (this.authors != null) {
      n = this.authors.size();
      for (Author author : this.authors) {
        result += author.getName();
        if (i < n - 1) {
          result += ", ";
        }
        i++;
      }
    }
    return result;
  } 
}

This makes sense in the case of a book, since the number of authors is in general limited to a small number. However, a Publisher may have a large number of published books. As a better alternative to our string serialization, we can use a JSF converter class which allows us to present the list of authors in a custom way. In our case, we choose to present it as list of names, where every name is presented on a separate line. For this, we implement the pl.m.converter.BookListConverter class and we annotate it with @FacesConverter as follows:

@FacesConverter( value="pl.m.converter.BookListConverter")
public class BookListConverter implements Converter {
  @Override
  public Object getAsObject( FacesContext context, 
      UIComponent component, String value) {
    return null;  // this method is not needed
  }
  @Override
  public String getAsString( FacesContext context, 
      UIComponent component, Object value) {
    String result = "";
    int i=0, n=0;
    if (value == null) {
      return null;
    } else if (value instanceof Set<?>) {
      n = ((Set<Book>) value).size();
      for (Book b : (Set<Book>) value) {
        result += b.getTitle();
        if (i < n-1) {
          result += "<br />";
        }
        i++;
      }
      return result;
    }
    return null;
  }
}

Then, we use the converter attribute in the facelet to specify that our converter class, e.g., pl.m.converter.BookListConverter, has to be used when the respective view component needs to be rendered:

<h:column>
  <f:facet name="header">Published books</f:facet>
  <h:outputText value="#{p.publishedBooks}" escape="false" 
    converter="pl.m.converter.BookListConverter"/>
</h:column>

Since the serialization text contains HTML elements, i.e., <br /> to produce HTML new lines, we have to specify the @escape="false" attribute, otherwise < and > will be replaced with the corresponding &lt; and &gt; entities.

The main advantage of using JSF converters is that we do not mix model code with view specific code, so our model classes remain clean and later, if we like to replace JSF with another UI technology, then our model classes remain unchanged, and we just need to take care of a corresponding converter for the new technology.