14 Replies Latest reply on Feb 9, 2007 6:57 PM by gavin.king

    SMPC and forcing a transaction to commit

    smokingapipe

      Here's the situation: I have a Seam SFSB which creates an entity and persists it. It does this using a SMPC. The method that does the persisting also sends the same entity to a message driven bean (MDB) on this same server. The MDB needs this entity to be attached so it can look at various fields in the entity.

      Here's the problem: The transaction from the seam-side, which sends the message, does not commit until the method ends (returns). But the MDB is picking up the message before that happens, so the MDB is seeing the entity before it has been committed.

      This means that it can't find the entity and it's acting as an unattached entity. This is bad. I can work around it by treating it as an unattached entity but I would prefer to be able to use it attached.

      Is there a solution for this? I would really like to mark my Seam SFSB method with some annotation that says, "let me handle the transaction on this one". I saw the @Transactional annotation, which says "method invocations should take place in a transaction, and if no transaction exists when the method is called, a transaction will be started just for that method", which is exactly what I don't want.

      Ideas?

        • 1. Re: SMPC and forcing a transaction to commit
          smokingapipe

          Actually one crazy thing I could do is inject the plain old EntityManager that isn't the SMPC and use that to do the persist, and then I suppose the MDB will see the Entity, because it will be using the same EM. Would that work?

          • 2. Re: SMPC and forcing a transaction to commit
            smokingapipe

            Tried that and it didn't work. Obviously even if I'm using the old @PersistenceContext annotation, I'm still bound to use the transaction created by Seam or the container so I can't do a commit() while the method is still running. Hmmm.

            • 3. Re: SMPC and forcing a transaction to commit
              smokingapipe

              I just now tried to use an @Observer. It was in the same class as the class that is doing all this. Anyway, the @Observer got triggered but it acted just like a regular function call, in other words, it happened within the method's transaction.

              Surely there's some way to do some things AFTER a transaction has committed?

              Btw I upgraded to Seam 1.1.1 to see if that would help me.

              • 4. Re: SMPC and forcing a transaction to commit
                smokingapipe

                And I tried something like this:

                @TransactionManagement(TransactionManagementType.BEAN)
                public class .....
                
                 @Resource SessionContext sessionContext;
                
                 public void beanSend() {
                 final UserTransaction userTransaction = sessionContext.getUserTransaction();
                 try { userTransaction.begin(); }
                 catch(NotSupportedException nse) { logger.severe("NotSupportedException: " + nse); }
                 catch(SystemException nse) { logger.severe("SystemException: " + nse); }
                 smpc.persist(myEntity);
                 try { userTransaction.commit(); }
                 catch(RollbackException nse) { logger.severe("RollbackException: " + nse); }
                 catch(HeuristicMixedException nse) { logger.severe("HeuristicMixedException: " + nse); }
                 catch(HeuristicRollbackException nse) { logger.severe("HeuristicRollbackException: " + nse); }
                 catch(SystemException nse) { logger.severe("SystemException: " + nse); }
                


                and it still doesn't work. The entity persists just fine but the MDB can't find it in its PersistenceContext.

                I'm quite baffled by how to force something to really really commit and really reall flush and whatever else.


                • 5. Re: SMPC and forcing a transaction to commit
                  smokingapipe

                  I gave up on it, and put everything that I need into Serializable fields. This is unfortunate that I can't find out how to attach it to an EntityManager from within the MDB, but I've tried a bunch of stuff all evening and nothing works. Any suggestions would be welcome.

                  • 6. Re: SMPC and forcing a transaction to commit
                    smokingapipe

                    And just for fun I also tried events.raiseTransactionSuccessEvent() and that just threw a NullPointerException. I would guess that that method would raise an event that happens AFTER the method / transaction is finished, which is what I want to happen, but it's just throwing NullPointerException and I'm not sure why.

                    • 7. Re: SMPC and forcing a transaction to commit
                      gavin.king

                      This is very dangerous stuff. Hibernate entities should never be used in two concurrent transactions/persistence contexts. You will need some kind of DTOish thing to work around.

                      • 8. Re: SMPC and forcing a transaction to commit
                        gavin.king

                         

                        "SmokingAPipe" wrote:
                        And just for fun I also tried events.raiseTransactionSuccessEvent() and that just threw a NullPointerException. I would guess that that method would raise an event that happens AFTER the method / transaction is finished, which is what I want to happen, but it's just throwing NullPointerException and I'm not sure why.


                        You have to install <core:transactionListener/> like in the booking demo.

                        • 9. Re: SMPC and forcing a transaction to commit
                          smokingapipe

                          Ah it's always the little things that make the big differences. I'll try that.

                          • 10. Re: SMPC and forcing a transaction to commit
                            smokingapipe

                            I tried that and IT WORKED! The message-sending method gets called after the transaction has committed successfully and then the message processor can see the entity in the persistence context, as is needed.

                            My one suggestion would be, in the raiseTransactionSuccessEvent, if the components.xml is not correct, instead of throwing a mysterious NullPointerException, maybe give a little more info, like a message that says "is core:transactionListener in your components.xml file?" I say this because this is a problem that will come up any time a Seam component method sends an entity to a message queue and the message queue needs to attach the entity. That's something that should be a fairly common usage in cases where messages are being used.

                            • 11. Re: SMPC and forcing a transaction to commit
                              smokingapipe

                               

                              "gavin.king@jboss.com" wrote:
                              This is very dangerous stuff. Hibernate entities should never be used in two concurrent transactions/persistence contexts. You will need some kind of DTOish thing to work around.


                              Wait are you saying that even if I use this events trick to send the message AFTER the transaction has committed, that's still dangerous? Because it's working fine right now, but that doesn't mean it's correct or stable, so I'm curious.


                              • 12. Re: SMPC and forcing a transaction to commit
                                gavin.king

                                I already fixed this in CVS this morning. This is something I usually do when users run into NPEs in the forum ;-)

                                What you are doing now is fine. There is no chance of concurrency.

                                • 13. Re: SMPC and forcing a transaction to commit
                                  smokingapipe

                                  Cool, thanks for letting me know because I would just be guessing otherwise.

                                  Now my final dumb question: What's the right way to re-attach an entity? This entity has been serialized, so all its collections have been lost (they are marked transient). If I go entityManager.merge(myEntity) I think that will not do what I want. .refresh() won't do it either because that is to be used for entities that are currently attached. I could do myEntity = entityManager.find(MyEntity.class, myEntity.getId()). Is that the right or best way to attach something in this case?

                                  • 14. Re: SMPC and forcing a transaction to commit
                                    gavin.king

                                    merge() is the correct way