3 Replies Latest reply on Aug 7, 2009 11:02 PM by Arbi Sookazian

    Proper way to do Application-scoped SMPC?

    Brandon Simpson Newbie

      I need to load some application-wide data from the DB on startup, so I created an application scoped POJO with @Startup annotation and just used @In EntityManager as usual. This appears to work well, but as I couldn't find any documentation, I was curious if this is the proper way to do it...I'm curious how Seam manages this persistence context and associated transactions since it's not associated with a user/conversation like the normal SMPC.


      Also, a question similar to this one...is there any problem with creating/using more than one conversation-scoped SMPC simultaneously and how does this affect transactions, etc.? This came up in a previous discussion...often a user will have the ability to interactively edit several items on a page, but only a subset of what is edited should be synched to the DB (for example, only the data that they actually elect to Submit/Save). If these edits are all held in a SMPC, there doesn't appear to be a good way to just synch part of the changes to the DB...it's sort of all or nothing. So one solution is to use a 2nd SMPC and attach what you really want to be synched to this 2nd SMPC and only use the 1st SMPC to manage the interactive editing. I hope this makes sense.

        • 1. Re: Proper way to do Application-scoped SMPC?
          Arbi Sookazian Master

          Using the @Startup with @Scope(ScopeType.APPLICATION) is a pattern for general application initialization routines.


          The following does work in Seam 2.1.2:


          @Startup
          @Scope(ScopeType.APPLICATION)
          @Name("testLoadOnStartup")
          public class TestLoadOnStartup {
               
               @In
               private EntityManager em;
               
               @Logger
               private Log log;
               
               @Create
               public void startup(){
                    List list = em.createQuery("from Hotel").getResultList();
                    log.info("list.size() = "+list.size());
               }
               
          }



          Log:


          10:01:25,230 INFO  [STDOUT] Hibernate: 
              select
                  hotel0_.id as id121_,
                  hotel0_.address as address121_,
                  hotel0_.name as name121_,
                  hotel0_.state as state121_,
                  hotel0_.country as country121_,
                  hotel0_.price as price121_,
                  hotel0_.city as city121_,
                  hotel0_.zip as zip121_ 
              from
                  Hotel hotel0_
          10:01:25,465 INFO  [TestLoadOnStartup] list.size() = 20



          Your 2nd question is something that is actually a pretty popular problem in the Seam community regarding SMPC management.


          I posted a very similar question in this forum regarding the scenario where you have a main form in the JSF page and multiple rich:modalPanels with forms embedded inside them.  So basically, the scenario is that CUD (create, update, delete) is possible when user clicks more than one save button on more than one form.  If you use only one SMPC associated with one EntityManager, then when the user clicks save button in the modal, then it will persist the changes in the main form as well (and most likely the user does not want and does not expect that to happen).


          A similar scenario is where you have multiple inputtable forms on the same main JSF page (no modals here).  So once again there are multiple save buttons, etc.  What is the best approach for isolating CUD to each form so when we save changes in form1 it does not cause pesisting to DB of changes in formX as well.


          What we've done is use one EntityManager and one SMPC for the main form on the JSF page and a different EntityManager and different SMPC for the modalPanels, thus isolating the CUD operations in both cases.


          There was an interesting Hibernate Scaling video on infoq.com or tss.com recently by Emanuel Bernard and he mentioned that a persistence context may take around 20MB of space in the JVM, but it obviously depends on your db schema, etc.  Just remember that the PersistenceContext serves as a 1st level cache and thus any entities you load and are managed in the PC are cloned and cached for dirty checking (this is how you can load entities via a JPQL query into the PC, present them in a form for changes by the user and then simply flush() the PC and the update is issued to the DB by Hibernate without you having to write one update statement by hand).


          I believe in the Seam ref doc somewhere it states that you are allowed to use more than one SMPC.  If you think about it, sometimes you may be forced to use more than one SMPC (e.g. you need to do CRUD on more than one DB in one Seam app).


          But as far as best practices are concerned in these scenarios, I'm not sure if there's an official wiki, etc. or coverage in a book or Seam distro project example.


          HTH.

          • 2. Re: Proper way to do Application-scoped SMPC?
            Brandon Simpson Newbie

            Thanks for the detailed response, Arbi!


            The application-scoped example you posted is pretty much the same as what I came up with, so probably that is a good way to do it. I was just curious of what goes on behind the scenes in that case, since entityManager is normally conversation scoped, but in the case of an application-scoped component, there doesn't seem to really be a conversation scope since the component is created independently of any user. So I was just wondering what Seam was really doing behind the scenes in a situation like that and if it wrapped it in a single transaction or what.


            Your second example is exactly the kind of situation I was talking about. I figured you could use multiple SMPCs, but again I'm curious how Seam is handling this. Does it still wrap everything in transactions the same way and can it cause any bad side effects? In any case, sounds like it works also, so that's good to know.


            Since you seem really knowledgable about all this, I'm wondering how you handle another potential problem I thought. Suppose a user does some editing on a modal panel, main form, etc. and those edits are stored in persistence context in a long running conversation (I'm assuming you would need a long running conversation if the page allowed interactive editing). If your website uses a template that has global links on it and you want to prevent the long running conversation and SMPC from being carried over to the next page if the user clicks one of the global links, how do you do it? For example, say a user makes changes on page A but doesn't save them, then clicks a global link that navigates to page B. All the edits from page A would still be in the persistence context and would be visible (or invisible but still present) on page B unless you end the LRC, right? Only thing I can think of is to end the conversation before redirect, but then I'm guessing things that I want to carry over to the next page like FacesMessages will be left behind. What I really want is a way to just get a completely new SMPC without messing with the old one (since the user may return to editing this page later). Would this be another case where it makes sense to have more than one SMPC, perhaps under a different name?


            Thanks again for your response!


            Brandon

            • 3. Re: Proper way to do Application-scoped SMPC?
              Arbi Sookazian Master

              As far as the global link scenario is concerned, I haven't had to deal with that (yet).  Most likely you'd need to leave/abandon the LRC (propagation=none) and then start a new LRC on the next page/action.  So now you have a new active LRC and a background LRC due to timeout in X seconds/minutes.


              <s:link view="/foo.xhtml" propagation="none"/>


              When they return to the original page A, you'll have to switch in the corresponding LRC for that page.  This may involve some API interaction with ConversationEntries and ConversationEntry.  There is a workspace (conversation) switcher example in the booking distro example app which is for the user to select from a HtmlSelectOneMenu component (drop down).  In this case there would be no UI component.


              You will have to manually keep track of which LRCs correspond to which pages and I'm not sure if there's a standard way of doing this.


              If you think about it, if you're using a different SMPC pointing to the same db, then it's most likely for a different use case.  So you may want to design you UI, if possible, to constrain the user to one use-case at a time to simplify SMPC management.


              I may end up being corrected in this post...