2 Replies Latest reply on May 28, 2010 2:44 PM by jan.kadlec

    question regarding seam global transaction, how to rollback

    cosmicduz

      Hi there,


      Perhaps I'm a bit slow but I've read the Seam Framework books (both edition), Seam in Action book, and Seam reference manual quite carefully, but I still don't quite understand seam transaction.


      Here's the situation: I'm using Seam in JavaEE 5.0 environment (Websphere 7.0), but I don't use any EJB3 SB or MDB, only Entity and Seam POJO. I package the application in an EAR package. All the .xhtml files, components.xml, pages.xml are in the WAR. All java class files, entity beans, seam.properties, persistence.xml are in EJB JAR archive. All dependency libraries are in the root of the EAR. Classpath manifests are configured in such a way that these libraries are visible to both EJB JAR and WAR files. My components.xml is minimal with just these few lines



      <components ... >
           <core:manager concurrent-request-timeout="500" conversation-timeout="120000"/>        
          <persistence:entity-manager-factory name="PU" persistence-unit-name="PU" startup="true"/>
          <persistence:managed-persistence-context name="entityManager" auto-create="true" entity-manager-factory="#{PU}"/>
      </components>




      My persistence.xml goes like this:



      <persistence ... >
           <persistence-unit name="PU" transaction-type="JTA">
                <jta-data-source>jdbc/myDataSource</jta-data-source>
                ...
           </persistence-unit>
      </persistence>



           
      I use @In to inject the EntityManager.


      Please clarify for me this question: what sort of transaction management is being used in this situation and how do I control the transaction?


      From what I've read I would assume that I have Seam's JTA global transactions as depicted in page 371 of Dan Allen's Seam in Action book, with one transaction starting at Restore View phase, and continuing all the way to the end of Invoke Application phase. If that is the case, how do I roll back this transaction when something goes wrong?


      To access entity bean, I would use Seam POJO bean in conversation scope, and inject an EntityManager using @In, retrieve an entity and assign it to a property of the Seam POJO. So in my facelet xhtml page I would have something like this:


      <h:inputText value="#{mySeamBean.myEntity.aProperty}">
           <a4j:support event="onblur"/>
      </h:inputText




      Now, in MyEntity.setAProperty() methods I would set the property for this entity, and in many case, dependent properties for many other entities related to this one. This myEntity and related entities would be already managed by the current persistence context residing in the conversation scope, so all of them would be updated if everything goes right.


      But in some case, while setting properties for related entities, I would find some business rule being violated, and would want to abort the whole operation, revert all entities to their initial state. How do I do that? I tried to throw an exception annotated with @ApplicationException(rollback=true), the exception is kept thrown out all the way and is never caught in my code, but the roll back doesn't happen. In fact the exception is not even visible to the user. It gets silently swallowed and only seen in the log file. It's shown in the log file that the exception is thrown in Update Model Values phase, and the Invoke Application phase is skipped all together, whatever model data being updated before the exception persisted, and the Render Response phase proceed as if nothing happens.


      I tried this: I remove the throwing of the exception in the setters of the entity, and in the action method of the <a4j:support> component, I tried to explicitly rollback transaction with either Transaction.instance().setRollbackOnly() or Transaction.instance().rollback(). Neither worked, or even gave any error. The entities are still updated and flushed to the database (If the exception was still thrown in the setter method, then the action method of <a4j:support> would not get executed at all) Does this means Seam's managed global transaction can not be asked explicitly to roll back?


      I left the session idle for awhile, and when the conversation timed out (it's a long running conversation), I would get this in the log:




      [5/5/09 16:17:16:765 SGT] 00000005 SystemOut     O 16:17:16,765  WARN Component:1440 - Exception calling component @Destroy method: entityManager
      java.lang.RuntimeException: exception invoking: getTransaction
           at org.jboss.seam.util.Reflections.invokeAndWrap(Reflections.java:154)
           at org.jboss.seam.Component.callComponentMethod(Component.java:2211)
           ...     
      Caused by: javax.naming.ConfigurationException: A JNDI operation on a "java:" name cannot be completed because the server runtime is not able to associate the operation's thread with any J2EE application component.  This condition can occur when the JNDI client using the "java:" name is not executed on the thread of a server application request.  Make sure that a J2EE application does not execute JNDI operations on "java:" names within static code blocks or in threads created by that J2EE application.  Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on "java:" names. [Root exception is javax.naming.NameNotFoundException: Name "comp/UserTransaction" not found in context "java:".]
           at com.ibm.ws.naming.java.javaURLContextImpl.throwConfigurationExceptionWithDefaultJavaNS(javaURLContextImpl.java:426)
           at com.ibm.ws.naming.java.javaURLContextImpl.lookup(javaURLContextImpl.java:398)
           at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:214)
           at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:154)
           at javax.naming.InitialContext.lookup(InitialContext.java:455)
           at org.jboss.seam.transaction.Transaction.getUserTransaction(Transaction.java:79)
           at org.jboss.seam.transaction.Transaction.createUTTransaction(Transaction.java:71)
           at org.jboss.seam.transaction.Transaction.getTransaction(Transaction.java:44)
           ...
      Caused by: javax.naming.NameNotFoundException: Name "comp/UserTransaction" not found in context "java:".
           at com.ibm.ws.naming.ipbase.NameSpace.lookupInternal(NameSpace.java:1178)
           at com.ibm.ws.naming.ipbase.NameSpace.lookup(NameSpace.java:1095)





      This error only happened when the conversation timeout, not when it get terminated by @End annotation.
      If the UserTransaction is not available in JNDI, how did seam obtain a transaction at the beginning of the request in the 1st place? Or it did not get a transaction at all? But when I tried Transaction.instance().isActive in the a4j:support action method above, I got true. I don't understand any of these at all.


           
      Please help point out what I am doing wrong here. Seam seems very good in the books, but in actual application, nothing seems to work as I expect. I'm sure this would be due to my ignorance. I appreciate if someone can point it out for me.


      Thank you very much for your time.