Search code examples
javajsfprimefacesjboss7.xconversation-scope

Loaded Records not showing in view


So to make it relatively simple: I have some Primefaces-Page, which is supposed to represent database records in a table-structure.

I keep these records in a List<Customer> which resides in a @ConversationScoped backing bean. I have verified via debugging, that the List gets correctly filled with records from the Database (hibernate db FWIW). This is accomplished with the help of a "businessInterface" Distributor class, that is in fact nothing but a decoupling mask for (Database)Service-Classes.

As mentioned, I have verified that the Database, as well as the Distributor correctly return the expected values. Unfortunately in the View there are no records present and instead the emptyMessage is displayed.

The application is running on a jBoss 7.1.1-Final Application Server.

For better readability I have excluded the h:head, as well as h:body, ui:composition, ui:define and h:form around the provided code, as well as shortened the columns to the two different usages (property display and action exposition)

The View (shortened and truncated):

<ui:define name="inhalt">
    <p:growl id="msgGrowl" autoUpdate="true" showDetail="true" />
    <h:form onkeypress="if (event.keyCode == 13) {return false; }">
        <p:dataTable var="customeritem" id="customerTable"
            rowkey="#{customeritem.id}" value="#{customerListController.customerList}"
            paginator="true" rows="13" autoUpdate="true" 
            filteredValue="#{customerListController.filteredCustomers}" 
            emptyMessage="no customers found!" 
            sortFunction="#{customerListController.filteredCustomers}">

            <p:column sortBy="name" filterBy="name" headerText="Kunde"
                filterMatchMode="contains">
                <h:outputText value="#{customeritem.name}" />
            </p:column>
            <p:column>
                <f:facet name="header">
                    <p:commandButton value="Neuer Kunde"
                        action="${customerListController.addCustomer()}"
                        icon="ui-icon-plus" />
                </f:facet>
                <p:commandButton id="doViewDetailsButton" icon="ui-icon-clipboard"
                action="${customerListController.viewDetails(customeritem.getId())}" />
                <p:tooltip for="doViewDetailsButton" value="Details ansehen" />

            </p:column>
        </p:dataTable>

    </h:form>
</ui:define>

The Backing Bean:

@Named
@ConversationScoped
public class CustomerListController implements Serializable {

    private static final long serialVersionUID = -5961625401284927892L;

    private List<Customer> customerList = new ArrayList<Customer>();
    private List<Customer> filteredCustomers = new ArrayList<Customer>();

    @Inject
    CustomerEditController customerEditController;

    @Inject
    CustomerDetailsController customerDetailsController;

    @Inject
    CustomerDistributor businessInterface;

    public String addCustomer() {
        return editCustomer(0l);
    }

    public String editCustomer(long customerId) {
        setFilteredCustomers(null);
        customerEditController.recieveCustomerById(customerId);
        return Pages.CUSTOMER_EDIT;
    }

    public String viewDetails(long customerId) {
        setFilteredCustomers(null);
        customerDetailsController.recieveCustomerById(customerId);
        return Pages.CUSTOMER_DETAILS;
    }

    public String deleteCustomer(long customerIdToDelete) {
        businessInterface.delete(customerIdToDelete);
        setFilteredCustomers(null);
        fillCustomerList();
        return Pages.CUSTOMER_LIST;
    }

    @PostConstruct
    public void fillCustomerList() {
        customerList.clear();
        customerList.addAll(businessInterface.loadAll());

    }

    public List<Customer> getCustomerList() {
        return customerList;
    }

    public List<Customer> getFilteredCustomers() {
        return filteredCustomers;
    }

    public void setFilteredCustomers(List<Customer> filteredCustomers) {
        this.filteredCustomers = filteredCustomers;
    }
}

This used to work, when I had the Backing Bean in @SessionScoped, but as that required hackish workarounds to produce intuitive (and expected) behavior I decided to move the Backing Bean to a smaller scope. I therefore chose the @ConversationScoped, because the BackingBean needs to stay longer than the request lifecycle... (Also running a query against a db for every request is damn expensive...)

A short explanation on the CustomerEditController and CustomerDetailsController. They are the responsible ones for Editing and Showing further information on the single records if they are requested by clicking one of the Buttons.

The non-working stuff is the @PostConstruct public void fillCustomerList(). Everything else works as expected...

If you need any further information, please ask, I will provide context as needed ;)


Solution

  • I have found a successful workaround for this, but it's extremely hackish and I really dislike the approach, as it introduces additional behavior in a getter. I modified the Backing Bean as follows:

    public List<Customer> getCustomerList() {
        if (customerList.size() == 0) {
            fillCustomerList();
        }
        return customerList;
    }
    

    But let me state this again,

    this is definitely not the desired behavior and not a good approach at solving this problem.

    EDIT:

    I found a different fix after a little more digging and a lucky link. I modified the backing bean as follows:

    @Inject
    Conversation conversation;
    
    @PostConstruct
    public void init() {
        if(conversation.isTransient()) {
            conversation.end();
        }
        conversation.setTimeout(120000);
        conversation.start();
    }
    

    and now It works even without the hackish behavior in the getter (as demonstrated above).