1 2 Previous Next 21 Replies Latest reply on Feb 28, 2008 10:21 PM by christian.bauer

    What if I want programmatic control of conversations?

    franciscoperedo

      Hi!


      What if I want to control conversations programatically...
      I can not find any examples on how to do that...
      I found: org.jboss.seam.core.Conversation and it seems to have  methods for everything... except rollbacking all changes made during the transaction...


      So... is it safe to use org.jboss.seam.core.Conversation ?


      Regards,
      Francisco

        • 1. Re: What if I want programmatic control of conversations?
          jazir1979

          Yes, it is safe and it is discussed in the documentation http://docs.jboss.com/seam/2.0.0.GA/reference/en/html_single/#d0e16590


          It doesn't provide rollback support because transactions and conversations are in fact seperate issues.  If you are using CMT SFSB Seam components, then each method call is a transaction, whether you are in a long-running conversation or not.


          Of course, if you are using conversations with flushMode MANUAL, your changes are only flushed to the DB (not committed) when you choose.

          • 2. Re: What if I want programmatic control of conversations?

            Thanks, and, what if I combine org.jboss.seam.core.Conversation and org.jboss.seam.transaction.Transaction...
            Could I control both of them programatically?
            Is it safe to use org.jboss.seam.transaction.Transaction?


            Basically I would like to be able to start a ConversationalTransaction programmatically (a Transaction that spans a conversation) and to be able to programmatically End this ConversationalTransaction either by commiting anything that happened inside it or reverting everything...


            Ever tried to do that?


            Thanks again


            Regards,
            LuxSpes

            • 3. Re: What if I want programmatic control of conversations?
              jazir1979

              Hi there,


              I think you'll be able to do it, but I'm sorry I've never really done that kind of explicit transaction management, perhaps one of the Seam folk will be able to answer.


              The way I do this is by using the MANUAL flush mode, and only calling em.flush() in successful scenarios.  That way, regardless of transactions, your changes won't go to the DB unless you call flush.  But I'm in a CMT bean, and perhaps our scenarios are too different.


              good luck,
              Daniel.

              • 4. Re: What if I want programmatic control of conversations?

                Hi!
                Yes em.flush() and MANUAL work to achieve this effect... except for one (in my case very common) case: Inserting new pojos... because of the required generation of the primary key, inserts are directly sent to the database...
                How do you handle that?


                Thanks


                Regards,


                LuxSpes

                • 5. Re: What if I want programmatic control of conversations?
                  jazir1979

                  hmmmm, i don't handle that!  that's a good point.  in my case i don't really have a situation where the creation of an entity can't be done atomically, so i guess i'm lucky :)


                  I mainly use this so that user's can manipulate on-screen data via Ajax4JSF but not have anything saved/flushed until they click the primary save button on the page.  Saving a new entity is also done via that save action, and can't be rolled back once it's done.


                  I imagine for anything more advanced you do need to try the explicit transaction management.  I'd be interested to hear what your use case is that makes this common, though?  I'd have thought you'd be able to hold off calling em.persist() for the new entity until the final step/txn.


                  cheers,
                  Daniel.

                  • 6. Re: What if I want programmatic control of conversations?

                    Hi, well you know, it is the typical CRUD stuff,
                    you want to save a new Person and that Person has many Addresses, the person needs to have at least one address or it is useless (you  have a requirement not to allow to save a Person that you will not be able to find) so, you create your Person class, you Address class, you connect them in OneToMany, you ask seam-gen to generate the CRUD screens...
                    And then it turns out that unless you save the Person you can not add Addresses to it!


                    You start reading the code generated and you realize the solution its easy, just add the new Person to the entityManager right from the beginning... but then there is no way to cancel, that person will go straight to the database...


                    So you start thinking about rollbacking the transaction, but it is not as easy as it seams...


                    And, even when (if) I make it work, I might have further problems, specifically validation problems (like for example, what happens if a field can not be empty, and you want the validation to run before commiting the transaction not in the moment you make entityManager.persist(person))


                    So.. it basically is a very interesting problem:
                    How to save only after you have a full graph of objects that pass validation before commit (And not before insert) and allow the user to save all, or cancel all changes. (And how to do it without having to build a custom different solution again and again each time you need a SaveAll and a CancelAll)


                    I am thinking that maybe the problem is the EntityManager... maybe it really doesn't play well with the idea of UnitOfWork... maybe I need to wrap it in an EditingContext ...
                    (I really still miss WebObjects some times)


                    Regards,


                    Luxspes










                    • 7. Re: What if I want programmatic control of conversations?

                      Where does the unit of work start?


                      From what I read in seam docs, the conversation is the unit of work... if that is so... then... why it is so hard to begin a transaction when the conversation starts, and end it when it ends?
                      How do I link a conversation and a transaction?
                      I had been looking in the examples provided by seam, but they always seem to use transactions or manual flush mode for the update  cases, not for the insert new object cases...


                      It's like there is no standard way to handle objects that will be added to the entityManager...

                      • 8. Re: What if I want programmatic control of conversations?
                        damianharvey.damianharvey.gmail.com

                        It really depends on how you want to handle this.


                        a) You could use AUTO (default) flush by handling it all on one page and using something like a rich:modalPanel to add an Address to a Person using richFaces/ajax4JSF. Then you just persist the Person when you're done. You haven't left the page so the conversation hasn't had to span multiple pages.


                        or


                        b) You add the Address in a separate page but don't want to save the Person just yet. To span pages like this you'll need to have MANUAL flush and decide where you end the conversation and flush the Person.


                        Cheers,


                        Damian.

                        • 9. Re: What if I want programmatic control of conversations?
                          jazir1979

                          I agree with Damian.  In my case we use option a), and just persist the Person when we are done - cascading takes care of the Addresses for us.  There is no issue about trying to save Addresses before Person, or having to rollback a Person.


                          I don't really see the problem here but it might be because I am not working with seam-gen'ed code, our code is all developed from scratch.


                          • 10. Re: What if I want programmatic control of conversations?

                            Hi!
                            The thing is in my team we are modifying the free marker templates for seam-gen'ed code, and we want to come with a generic way to handle single-save-all CRUD that we can use...


                            Another problem is that everybody seems to resolve this problem a little different... Hibernate helps standardize the way pojos are handled (sorting, filtering) but only after they are persisted, so, if you want to provide a sandbox for your user so that they feel safe making modifications (because they know, that at the end they can always click cancel) and they can see all pages (without the system telling the, you have to write the FirstName or will not let you see the page where you capture the data for the Address)


                            I believe conversations would be a very powerful abstraction to deal with this problem... I believe that that traditional transaction APIs are as limited as the Session is when compared with Conversations... with TrasactionalConversations you code for insert or for update would be essentially the same... if you need to sort, you ask hibernate to sort (you don't have to care about the objects being new), if you need to filter, you can ask hibernate to filter (you don't have to write parallel logic to filter your new pojos, and to filter older ones) ... and if you realize everything was a mistake you can rollback easily using the exact same code in every case: Asking for a rollback


                            I think UIs that handle data would be a lot easier to build if this were possible... and I believe that maybe Seam is the shortest path to achieve it...


                            I think it would be a very useful patterns... do you agree? do you see any important caveats that I should deal with first?


                            Regards,

                            • 11. Re: What if I want programmatic control of conversations?
                              jazir1979

                              Hi Francisco,


                              I agree that it's a useful pattern, it could make life alot easier.  However, keeping long-running transactions going while users do their thing is the biggest caveat, in my opinion.  For scalability reasons.


                              The discussion of long units of work in the Hibernate docs talks about this- http://www.hibernate.org/hib_docs/entitymanager/reference/en/html/transactions.html


                              Of course, your method is the only way you'll get the ability to let Hibernate do sorting, filtering, etc all against the database while you are in this long transaction.  But I think to be practical, you need to compromise here and make it so that the user's Sandbox is transient entities until the final stage when you allow them to persist.


                              Just my opinion though, and it would be interesting to see what others think!


                              cheers,
                              Daniel.

                              • 12. Re: What if I want programmatic control of conversations?
                                keithnaas

                                Daniel, your assessment is spot on. 


                                In applications, I think users are used to clicking Save to persist their changes.  Its up to us to figure out how to do that in a web app without killing the applications scalability!




                                • 13. Re: What if I want programmatic control of conversations?

                                  Hi!


                                  But... in a database with Multiversion Concurrency Control (like Oracle, or PostgreSQL) having a transaction while users do their thing shouldn't cause any locks, that IMHO would be the biggest problem, and if my system will be used in an intranet environment by about 100 users, they shouldn't be a big performance problem... (and development would certainly be far easier).


                                  Beyond the fact that it might not be such a good idea for scalability... Basically I would like to understand why it doesn't work... what is wrong with this code:


                                  @Stateful
                                  @Name("TransactionalConversation")
                                  public class TransactionalConversationBean implements TransactionalConversation {
                                       
                                     @Logger private Log log;
                                     
                                     @PersistenceContext(type=PersistenceContextType.EXTENDED)
                                     EntityManager entityManager;
                                      
                                     private int value;
                                       
                                       @Begin
                                       public String begin()
                                       {
                                            //implement your begin conversation business logic
                                            log.info("beginning conversation");
                                            try{                              
                                                 log.info("Transaction type #0",Transaction.instance());
                                                 Transaction.instance().enlist(entityManager);
                                            } catch (Exception e){
                                                 throw new RuntimeException(e.getMessage(),e);
                                            }
                                            return "success";
                                       }
                                       
                                       @Transactional(value=TransactionPropagationType.MANDATORY)
                                       public String increment()
                                       {
                                            log.info("incrementing");
                                            value++;
                                            User user =new User();
                                            user.setName("User"+value);
                                            user.setPassword("Password"+value);
                                            entityManager.persist(user);
                                            return "success";
                                       }
                                       
                                       @Transactional(value=TransactionPropagationType.MANDATORY)
                                       public String crash()
                                       {
                                            try {
                                                 log.info("rollbacking");
                                                 Transaction.instance().rollback();
                                            } catch (Exception e){
                                                 throw new RuntimeException(e.getMessage(),e);
                                            }
                                            return "success";
                                       }
                                       
                                       //add additional action methods that participate in this conversation
                                       
                                       @End
                                       public String end()
                                       {
                                          //implement your end conversation business logic
                                          log.info("ending conversation");
                                            return "home";
                                       }
                                       
                                       public int getValue()
                                       {
                                            return value;
                                       }
                                       
                                       @Destroy @Remove                                                                      
                                       public void destroy() {}     
                                  }
                                  



                                  What is wrong, why if I click a command button to call begin(),
                                  another to call increment() and another to call crash() the user
                                  inserted by increment is not rollbacked? It might not be scalable.. but it should work... shouldn't it?


                                  Regards,


                                  LuxSpes

                                  • 14. Re: What if I want programmatic control of conversations?
                                    jazir1979

                                    You might be able to make that work if your SFSB uses BMT instead of CMT.  ie-


                                    @TransactionManagement(TransactionManagementType.BEAN)
                                    public class TransactionalConversationBean implements TransactionalConversation {
                                    



                                    Or avoid EJB altogether and use a regular Seam JavaBean component.

                                    1 2 Previous Next