4 Replies Latest reply on Mar 7, 2007 7:09 PM by enazareno

    DataModel, Factory and Stale Data

    smutchler

      Is it possible to use a SSB with a DataModel. It's funny that the examples in the books and the JBoss web site use a SFSB and then say it's not a good idea to use SFSB with Seam. The problem I have with SSB is that the data is often stale. If you modify it in another SB (or even the same SB) and then return to the page it is showing the stale data. There has to be a way to do this without using SFSB or resorting to using a conversation. I can do this with straight JSF easily.

      Your help is GREATLY appreciated!

      Here is my SFSB:

      @Stateful
      @Scope(ScopeType.SESSION)
      @Name("inventoryAction")
      public class InventoryActionImpl implements Serializable, InventoryAction {

      Logger log = Logger.getLogger(InventoryActionImpl.class);

      @DataModel
      private List inventory;

      @DataModelSelection
      @Out(required = false)
      private Car car;

      @PersistenceContext(type = PersistenceContextType.EXTENDED)
      private EntityManager em;

      @Factory(value = "inventory")
      public void fetchInventory() {
      CarDAO carDAO = new JPACarDAO(em);
      inventory = carDAO.filterByStatus(Car.STATUS_AVAILABLE);
      }

      public String buy() {
      return "buy_car_admin";
      }

      public String edit() {
      return "edit_car_info";
      }

      public String createNew() {
      car = new Car();
      return "add_car";
      }

      public String deleteInventory() {

      List toBeDeleted = new ArrayList();

      for (Car car : inventory) {
      if (car.isSelected()) {
      toBeDeleted.add(car);
      }
      }

      // delete from database
      CarDAO carDAO = new JPACarDAO(em);
      carDAO.delete(toBeDeleted);

      // refresh cached inventory (seems to work)
      // but I'm screwed if another SB updates the data
      fetchInventory();

      return null;
      }

      @Remove
      @Destroy
      public void destroy() {
      }

      }


      Thanks,

      Scott

        • 1. Re: DataModel, Factory and Stale Data
          tony.herstell1

          If I understand you well enough then....

          if you are going to use a SSB then you pretty much have to manage the data it contains if you are going to update the contents of that data in another conversation...

          I have added addOrganisationToExistingList and other "call back" methods that are called by "external" conversations that fix up the data in my SSB (see below - addOrganisationToExistingList)

          Caching is helping you not hit the database.. but you got to manage cleaning up the "various" caches if you do stuff invisible to it... if that makes sense.


          
          /**
           * @author Tony Herstell
           * @version $Revision: 1.4 $ $Date: 2007-02-08 00:05:32 $
           */
          public interface FindOrganisationController {
          
           public void initOrganisations();
          
           public String startFind();
          
           public String applyFilter();
          
           public void destroy();
          
           // GUI Support Methods
           public void selectedSearchCriteriaPanelChanged(ValueChangeEvent event);
          
           public void setSelectedSearchCriteriaPanel(String selectedPanel);
          
           public String getSelectedSearchCriteriaPanel();
          
           public String getALL_OPTION();
          
           public String getFILTERED_OPTION();
          
           public boolean getAscending();
          
           public void setAscending(boolean ascending);
          
           public String getSortColumn();
          
           public void setSortColumn(String sortColumn);
          
           public void physicalDontCareValueChanged(ValueChangeEvent event);
          
           public String getPhysicalValueRequiredPanel();
          
           public String getPHYSICAL_VALUE_REQUIRED();
          
           public String getPHYSICAL_VALUE_NOT_REQUIRED();
          
           public boolean getPhysicalDontCare();
          
           public void setPhysicalDontCare(boolean physicalDontCare);
          
          
           /*
           * Used by external rountines to update the session organisations list object.
           */
           public void addOrganisationToExistingList(Organisation organisation);
          
           public void updateOrganisationInExistingList(Organisation organisation);
          
           public void deleteOrganisationFromExistingList(Organisation organisation);
          
          }
          
          




          Gavin will probably have sleepless nights over this code... :)


          
           public void addOrganisationToExistingList(Organisation createdOrganisation) {
           log.info("> addOrganisationToExistingList");
           List<Organisation> organisationsList = new ArrayList<Organisation>();
           organisationsList.add(createdOrganisation);
           organisations = organisationsList.toArray(new Organisation[0]);
           sort();
           log.info("< addOrganisationToExistingList");
           }
          
           public void deleteOrganisationFromExistingList(Organisation deletedOrganisation) {
           log.info("> deleteOrganisationFromExistingList");
           List<Organisation> organisationsList = new ArrayList<Organisation>();
           for (Organisation eachOrganisation : organisations) {
           if (deletedOrganisation.getId().compareTo(eachOrganisation.getId()) != 0) {
           organisationsList.add(eachOrganisation);
           }
           }
           organisations = organisationsList.toArray(new Organisation[0]);
           log.info("< deleteOrganisationFromExistingList");
           }
          
           public void updateOrganisationInExistingList(Organisation updatedOrganisation) {
           log.info("> updateOrganisationInExistingList");
           List<Organisation> organisationsList = new ArrayList<Organisation>();
           for (Organisation eachOrganisation : organisations) {
           if (updatedOrganisation.getId().compareTo(eachOrganisation.getId()) != 0) {
           organisationsList.add(eachOrganisation);
           } else {
           em.refresh(eachOrganisation); // Force the update of the organisation in the em cache.
           if (eachOrganisation.getPicture() != null) {
           em.refresh(eachOrganisation.getPicture()); // Force the update of the picture (for the user) in the em cache.
           }
           log.info(" from the DBase (refreshed) => " + eachOrganisation);
           organisationsList.add(eachOrganisation);
           }
           }
           organisations = organisationsList.toArray(new Organisation[0]);
           applyCurrentQuery();
           log.info("< updateOrganisationInExistingList");
           }
          
          
          



          and inside my Crud Controller:

          @Stateful
          @Name("cRUDOrganisationController")
          @Conversational
          public class CRUDOrganisationControllerImpl implements Serializable, CRUDOrganisationController {
          


          Inject my SSB so I can call back to it.. to tell it its been updated!
           /*
           * When we update the organisation then we have to laise with the FindOrganisationsController to refine its list of organisations.
           */
           @In(create=true)
           @Out
           private FindOrganisationController findOrganisationController;
          
          


          And at the end of this external conversation, i call back to the SSB...

           @End
           @TransactionAttribute(TransactionAttributeType.REQUIRED)
           public String update() {
           log.info("> update");
           em.merge(organisation);
           em.flush();
           facesMessages.addFromResourceBundle("organisation_update_successful");
          
           /*
           * We need to update this organisation in the list of organisations in the session scoped FindOrganisationController
           */
           if (findOrganisationController != null) {
           log.info("Organisation sent to database => " + organisation);
           findOrganisationController.updateOrganisationInExistingList(organisation);
           }
          
           log.info("< update");
           return "findOrganisation";
           }
          



          Other prople may have other solutions.

          • 2. Re: DataModel, Factory and Stale Data
            tony.herstell1

            oops in above post SFSB and SSB are mixed up... Sorry. I am sure you can work through that.

            • 3. Re: DataModel, Factory and Stale Data

              Of course you're getting stale data -- you're using SESSION scope and an extended persistence context that never changes during the bean's lifetime. Same PC, same isolated view of the database for the whole session.

              Seam certainly doesn't discourage using Stateful Session Beans, it discourages using session *scope* for anything that isn't truly session-global.

              I recommend using stateless beans for simple search and view pages, and conversation scoped (the default) when you want to manipulate the results without re-querying.

              • 4. Re: DataModel, Factory and Stale Data
                enazareno

                Hi Scott,

                Here's what I did to get fresh data from the datamodel. Provide a method like this in your SFSB like so :

                 publc void refresh() {
                 inventory = null;
                 }
                


                You can call this from your other beans or you can call this in an event. This will force your SFSB to get fresh data.

                Regards,

                Elmo