views:

2207

answers:

2

hello, I am - unfortunally - working with jsf and I am experiencing a problem. I have a jsf page that displays a table with some data, using the <h:dataTable> component. Every row of this table has an <h:commandLink> with a Remove action to remove the item in that row. When I perform that action, the method in the backing bean is correctly called, the ArrayList that contains the table items is correctly uptaded, and the navigation method to come back in the same page where the table is, is correctly performed.

But when the page is reloaded the table is not updated. I see the same items that were in the table before the remove action.

If I reload again the page, now the table is updated.

It seems just like the view is rendered before that the backing bean has completed its updates.

How can I force to render the view just when the backing bean has completed its updates?

Here some code:

The commandLink in the jsf page, inside the dataTable:

<h:column>
<f:facet name="header">
    <h:outputText value="Rimuovi" />
</f:facet>
<h:commandLink action="#{servicesListBean.removeServizio}" title="Rimuovi questo servizio" onclick="if(!confirm('Vuoi davvero rimuovere questo servizio?')) return false">
    <h:outputText value="Rimuovi" />
</h:commandLink>
</h:column>

And here the method in the backing bean:

public String removeServizio() {
     this.servizioToRemove = (Servizio) getDataTable().getRowData();
     try {
  ServiziDAO.removeServizio(this.servizioToRemove.getId());
  } catch (Exception e) {
   // ...
  }

     return userSessionBean.goToElencoServizi(); 
    }

In this case, I gave request scope to the backing bean. I already tried to give it session scope and call a method to update the list, but the result is the same.

I hope I was clear enough. Thanks to everyone in advance.


UPDATE TO THIS QUESTION THAT CHANGES EVERYTHING

Thanks to everyone. As McDowell said, the problem was not in the time when view was rendered, and the problem was not JSF related at all.

What happened is that when I removed an item from the DB, I did not delete the row, I just set an end date to the item. This is the right way to do it in my application, but while in the query that setted the end date to the item I took the time using System.currentTimiMillis(), in the query that reloaded the uptaded list I used the sysdate of the Oracle server.

There are some fractions of second of difference between this two dates, and that's why I could see the updated list just when I reloaded the page: because one ore two second were passed!

Maybe this issue can be useful to someone else.

+2  A: 

It seems just like the view is rendered before that the backing bean has completed its updates.

The only way this could happen is if you did the work in another thread - but it doesn't look like you are doing that.

I am guessing, but it looks like a logic problem in your backing bean. Is your servicesListBean keeping state? That is:

  1. HTTP POST
  2. The RESTORE VIEW phase of the lifecycle causes servicesListBean to be created in the request scope (each phase of the lifecycle needs to iterate over every row)
  3. servicesListBean fetches the rows from the persistence store and they are cached in the bean state
  4. When we get to the INVOKE APPLICATION phase, removeServizio() removes the target row from the persistence store, but does not do anything about the cached bean state
  5. The RENDER RESPONSE phase renders the stale cached bean state
  6. If you refresh now, the bean gets recreated with new cached values and you see the correct state

This backing bean recreates the problem:

public class DeleteFromRowBean {

  private ListDataModel dataModel;

  public DataModel getList() {
    if (dataModel == null) {
      List<RowBean> list = PersistenceStore.fetch();
      dataModel = new ListDataModel(list);
    }
    return dataModel;
  }

  public String deleteCurrentRow() {
    if (dataModel == null) {
      throw new IllegalStateException();
    }
    RowBean row = (RowBean) dataModel.getRowData();
    PersistenceStore.delete(row.getId());
    return null; // no navigation required
  }

}

Modifying the list while the data table is iterating over it is not a good idea (you'll get concurrent modification exceptions). The easiest solution in my sample bean is just to release the reference to the cached data. The data table already has a reference to this for the duration of the iteration in the INVOKE APPLICATION phase; when the RENDER RESPONSE starts, the data table will call getList() again, causing the new state to be fetched from the persistence store.

public class DeleteFromRowBean {

  private ListDataModel dataModel;

  public DataModel getList() {
    if (dataModel == null) {
      List<RowBean> list = PersistenceStore.fetch();
      dataModel = new ListDataModel(list);
    }
    return dataModel;
  }

  public String deleteCurrentRow() {
    if (dataModel == null) {
      throw new IllegalStateException();
    }
    RowBean row = (RowBean) dataModel.getRowData();
    PersistenceStore.delete(row.getId());
    // flush cached data
    dataModel = null;
    return null; // no navigation required
  }

}
McDowell
A: 

If you don't explicitly redirect to "another" page (even if it's the same page), you will get old data. It's feature. Try something like:

public String removeServizio() {
    .
    .
    .
    userSessionBean.goToElencoServizi(); // if this redirects to needed page
    return null;
}
Slartibartfast