1 2 Previous Next 21 Replies Latest reply on May 22, 2013 8:52 AM by paul.robinson

    JTA 1.2 Implementation Work

    paul.robinson

      Introduction

      This document discusses the outstanding issues and design decisions relating to our implementation of the JTA 1.2 specification.

       

      The JTA 1.2 Spec and JavaDoc can be obtained from here. The implementation is being developed here.

       

      The two major changes are @Transactional (EJB-like transaction annotations for managed beans) and @TransactionScoped (A CDI scope tied to the lifecycle of the active transaction). Each of these is covered in it's own section bellow. There are some smaller changes too, for which it is yet to be determined if they require any code changes. These are all discussed in the "Other Changes" section.

       

       

      Current Status of Implementation

      Preview implementation in Narayana 5.0.0.M3 and WildFly 8.0.0.Alpha3.

       

      Certification

      Test against Java EE 7 CTS. Passes with a modification to the CTS. This modification should no longer be required. See here: https://issues.jboss.org/browse/JBCTS-1268

      Test against JTA 1.2 TCK (Only required to test that we comply with the "XAResource#end" issue). Awaiting confirmation that this passes.

       

       

      @TransactionScoped

      Implementing a new context in CDI is relatively straight-forward. You just need to implement the javax.enterprise.context.spi.Context interface. Here's the pseudo code for this impl:

       

       

      public class TransactionContext implements Context {
       
          public Class<? extends Annotation> getScope() {
              return TransactionScoped.class;
          }
      
           public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
      
                bean = (PassivationCapabale) contextual
                if (bean already in TSR) {
                     return bean
                } else if (creationalContext != null ) {
                     create new bean
                     add bean to TSR
                     return bean
      
                } else {
                     return null
      
                }
          } 
      
          public <T> T get(Contextual<T> contextual) {
              return get(contextual, null);
          }
      
          public boolean isActive() {
               return true if current transaction status in {STATUS_ACTIVE, STATUS_MARKED_ROLLBACK, STATUS_PREPARED, STATUS_UNKNOWN, STATUS_PREPARING, STATUS_COMMITTING, STATUS_ROLLING_BACK }
          }
      }
      

       

      Do we Implement from scratch or extend a Weld Abstract Context? [RESOLVED]

      Weld implements many different Contexts (SessionScoped, ApplicationScoped, RequestScoped, etc) and as a result many of the common implementation details have been abstracted into a largish class hierarchy, making the actual implementations of the specific Contexts much simpler. We may be able to extend one of these Abstract Contexts. However, it's not clear to me exactly which one should be extended. Also, this would place a dependency on Weld, which I'm not sure is a good idea. I think it would be better to simply depend on the CDI API, so that our implementation remains portable. Also the implementation of the TransactionContext is quite simple, so we may be over-engineering the solution by extending a Weld Context.

       

      We should implement this from scratch as any class we might extend is not part of a supported interface and so could be removed/changed without notice. See comment from @pmuir bellow.

       

      How are the out-of-scope beans garbage-collected? [RESOLVED]

      The 'isActive' method ensures that the context is recognized as inactive, outside of an active transaction. However, the TransactionalBeanStore will collect garbage over time and so needs clearing at some point after the Context is no longer active. These are the current options that I think we have:

       

      • TransactionSynchronizationRegistry. We can use the putResource and getResource methods to store objects against the current transaction. These instances seem to be garbage collected when the transaction completes.
      • AfterCompletion callback. We could register a synchronisation participant with the transaction and then clear the TransactionalBeanStore during afterCompletion. This should be fine as I don't think the Context is available in the afterCompletion callback. The spec does state that "Any Synchronization.afterCompletion methods will be invoked in an undefined context".
      • Bespoke invocation from the TM. If we have any problems with the previous solutions, we could simply make a direct call from the transaction manager at an appropriate point in the lifecycle of the transaction.

       

      We are going with the TSR approach.

       

      What's the best way to enable this feature in WildFly? [RESOLVED]

      The implementations asociated with @Transactional and @TransactionScoped are registered via a CDI Portable Extension. The naryana jar in the org.jboss.jts module needs to contain a refrence to the PE via the "/META-INF/services/javax.enterprise.inject.spi.Extension" file. The application can then have these PE registered by exporting the services when declaring the dependency on "org.jboss.jts". This is done by putting this line in the MANIFEST.MF: "Dependencies: org.jboss.jts export services".

       

      @Transactional

       

      Is something similar implemented elsewhere? [RESOLVED]

      There's currently two implementations (that I am aware of) that implement something similar:


      1. EJB 3. This implementation manages a JTA transaction, but is more EJB specific.
      2. DeltaSpike. This implementation seems to manage a hibernate transaction, but is more CDI specific.

       

      Neither seem close enough to be simply copied/re-used/etc. Therefore, my current thoughts are to implement our own, using the EJB 3 implementation to understand any corner-cases that I have not already thought of.

       

       

      How are these interceptors enabled by default? [RESOLVED]

      In CDI 1.0, all interceptors where disabled by default, with the application needing to specify which interceptors to enable. In CDI 1.1 interceptors are enabled by default by using @Priority. The JTA spec should tell us what value to use.

       

      How do I detect invalid usages of UserTransaction?

      According to the JTA 1.2 spec:

       

      If an attempt is made to call any method of the UserTransaction interface from within the scope of a bean or method annotated with @Transactional and a Transactional.TxType other than NOT_SUPPORTED or NEVER, an IllegalStateException must be throw

       

      In EJB 3 a deployment failure occurs if you attempt to inject a UserTransaction into a CMT bean. This is because EJB disallows the usage of UserTransaction for any method in a CMT bean, regardless of the transaction attribute used. However, we can't take this approach. With @Transactional, some methods may use the injected UserTransaction, when others may not. Therefore, I think we need to enable/disable the usage of UserTransaction at runtime.

       

      I think the way to do it is to somehow "mark" the UserTransaction as unavailable/available (depending on the TxType) in the interceptor, prior to proceeding the invocation. The old value (unavailable/available) is restored in a finally block, to ensure it is always restored. Pseudo code here:

       

       

      @AroundInvoke
      public Object intercept(InvocationContext ic) {
      
           oldState = //current availability of UserTransaction
      
      
           if (TxType in {NOT_SUPPORTED, NEVER}) {
                //Mark UserTransaction as available
           } else {
                //Mark UserTransaction as unavailable
           }
      
           try {
                ic.proceed();
           } finally {
                //Restore UserTransaction availability via oldState variable
           }    
      }
      

       

      I've simplified the above code to just show the enabling/disabling of UserTransaction.

       

      When injecting UserTransaction via the @Resource annotation, you get an instance of org.jboss.tm.usertx.client.ServerVMClientUserTransaction from JNDI. ServerVMClientUserTransaction lives in jboss-transaction-spi. Marking available/Unavailable could be done by calling methods on ServerVMClientUserTransaction (default is available, so this will not break any other places that use ServerVMClientUserTransaction). This would cause the availability to be stored in a ThreadLocal. Each method that implements UserTransaction would first check that the UserTransaction is available for use (throwing an IllegalStateException if not) and then continuing as before.

       

       

      Other Spec Changes

      Most other changes seem to be clarifications, that don't result in changes to the implementation.

       

      There is one other change that may impact the implementation:

       

      Added the following description to the end of Section 3.3.1, “ResourceEnlistment”: "A container only needs to call delistResource to explicitly dissociate a resource from a transaction and it is not a mandatory container requirement to do so as a precondition to transaction completion. A transaction manager is, however, required to implicitly insure the association of any associated XAResource is ended, via the appropriate XAResource.end call, immediately prior to completion; that is before prepare (or commit/rollback in the onephase-optimized case)."

       

      It looks like we already call XAResource#end for all XAResources, just not all at the same time.

       

      In com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple#commit (and #rollback), endSuspendedRMs() is invoked. This calls XAResource#end on all those in state TxInfo.ASSOCIATION_SUSPENDED.

       

      Later, in com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord#topLevelPrepare (and also in topLevelOnePhaseCommit and topLevelAbort) XAResource#end is invoked for resources that are not in either of the states TxInfo.NOT_ASSOCIATED and TxInfo.FAILED (notice call to endAssociation()).

      Also, it looks it was intentional to ensure end is always called on an XAResource prior to prepare, abort or onePhaseCommit. Notice the comment: "end has been called so we don't need to do it again!".

       

      Therefore, we already comply to the spec statement that:

       

      A transaction manager is, however, required to implicitly ensure the association of any associated XAResource is ended, via the appropriate XAResource.end call, immediately prior to completion; that is before prepare (or commit/rollback in the onephase-optimized case). 
      

       

       

       

       

      Impact on WildFly

       

      Update the jboss-transaction-api_spec version [RESOLVED]

      Shelly is in the process of updating jboss-transaction-api_spec to version 1.2. This should be done soon.

       

      Java EE 7 Certification

      The two major changes brought in JTA 1.2 are not covered by the JTA 1.2 TCK. Therefore, I will need to wait for the availability of the Java EE 7 CTS to certify this work. In the mean time, I'll write some simple tests to make sure I'm on track.

       

       

      General Issues

      When something goes wrong internally to the implementation in the TransactionalContext, what RuntimeException should I throw? [RESOLVED]

      TransactionalException is the only RuntimeException in the JTA 1.2 spec, and that does not seem apropriate here. Therfore, I'll just use whatever general unchecked exception we use in the JTA 1.1 implementation.

       

      Where should I place the implementation and tests? [RESOLVED]

      The code for this work should be placed under "$NARAYANA_SRC/ArjunaJTA/cdi". This ensures that we don't pollute the pure JTA (ArjunaJTA/jta) code-base with dependencies that don't make sense in a standalone scenario. It also keeps the Arquillian tests seperate from the unit tests in ArjunaJTA/jta; which is good as they need to be ran later in the testing cycle (i.e. after we have built WildFly).

        • 1. Re: JTA 1.2 Implementation Work
          marklittle

          We already delist (call end) if the transaction is completed and the container hasn't done the delist already.

          • 2. Re: JTA 1.2 Implementation Work
            paul.robinson

            Thanks Mark.

            • 3. Re: JTA 1.2 Implementation Work
              pmuir

              You should implement the context from scratch.

               

              Weld has a clearly (and carefully) defined split between API/SPI and impl. If a class is in the weld-api or weld-spi, you can be sure that we follow the rules around deprecation and api stability, and do not change these apis from one micro release to another, and deprecate between minors etc. If it's in weld-impl, you can expect it to change without warning or recourse!

               

              You should be implementing AlterableContext as you are targeting CDI 1.1. This gives you a destroy callback, which may be useful for cleanup.

               

              Ask Stuart Douglas about activation.

               

              I would write @Transactional from scratch.

               

              Use @Priority to activate by default. The JTA spec should tell you what value to use.

              • 4. Re: JTA 1.2 Implementation Work
                marklittle

                What I said was only partially correct. We call end on suspended RMs if delist hasn't been called. Having this happen for all RMs is easy, but we'd have to be aware of interface changes and semantic changes, since it is not backwardly compatible *unless* we have this as a configurable option. Worth considering though, since JTA 1.2 adoption will take a while.

                • 5. Re: JTA 1.2 Implementation Work
                  tomjenkinson

                  What should the default be for the configurable 3.3.1 delist? I would expect out of the box that the JTA 1.2 behaviour would apply, do you agree?

                  • 6. Re: JTA 1.2 Implementation Work
                    tomjenkinson

                    RE:

                    Where should I place the Arquillian tests?

                     

                    More generally, couldn't the interceptors go in a new module: ArjunaJTA/cdi This is because the raw jta/ module now has some dependencies that may not make sense in a standalone scenario (but would be acceptable in narayana-jta uber jar).

                     

                    It still a good question about when you can actually execute the test phase as you need the container to have its version of Narayana replaced, currently we have to do these tests in scripts/hudson/narayana.sh after we have built the AS with Narayana in it. Maybe these cdi integration tests  should be moved to qa/ somewhere (on a related note, perhaps the XTS tests should exist under there too)? The AS for example has a "testsuite" hierarchy for integration tests.

                     

                    Tom

                    • 7. Re: JTA 1.2 Implementation Work
                      tomjenkinson

                      Re:

                      How are the out-of-scope beans garbage-collected?

                       

                      I would say that the afterCompletion seems to be the most intuitive solution rather than a new SPI. As you say, this should be compatible with the wording around not being able to rely on the context at this point.

                      • 8. Re: JTA 1.2 Implementation Work
                        tomjenkinson

                        Re: What's the best way to enable this feature in WildFly?

                        The Context needs to be enabled via a CDI Extension. I'm currently enabling this via a configuration file (javax.enterprise.inject.spi.Extension) placed in the META-INF of the application. I think this is the way to do this for applications that run in a simple CDI container. However, we can remove this burden for applications deployed to WildFly. It's quite simple to enable this Extension in a DeploymentProcessor of the transactions SubSystem. However, we would need to either, enable it for all deployments, or just those that use @TransactionScoped (I don't yet know how to detect this).

                         

                         

                         

                        Does enabling the extension when an application does not use it add overhead to the application? Maybe provide a way to disable it rather than enable it if so?

                        • 9. Re: JTA 1.2 Implementation Work
                          tomjenkinson
                          Re: When something goes wrong internally to the implementation of the TransactionInterceptor or TransactionalContext, what RuntimeException should I throw?

                          It doesn't seem to be specified, so I'm thinking I can just throw some Narayana specific RuntimeException.

                           

                          Can you provide an example, at the moment I am thinking javax.transaction.TransactionalException (from 3.7: Transactional Annotation)

                          • 10. Re: JTA 1.2 Implementation Work
                            paul.robinson

                            Thanks Pete,

                             

                            I've applied your comments to the document.

                            • 11. Re: JTA 1.2 Implementation Work
                              paul.robinson

                              Mark,

                              What I said was only partially correct. We call end on suspended RMs if delist hasn't been called. Having this happen for all RMs is easy, but we'd have to be aware of interface changes and semantic changes, since it is not backwardly compatible *unless* we have this as a configurable option. Worth considering though, since JTA 1.2 adoption will take a while.

                              Thanks, I'll have a think about this and discuss further with you and the rest of the team.

                              • 12. Re: JTA 1.2 Implementation Work
                                paul.robinson

                                Tom,

                                Where should I place the Arquillian tests?

                                 

                                More generally, couldn't the interceptors go in a new module: ArjunaJTA/cdi This is because the raw jta/ module now has some dependencies that may not make sense in a standalone scenario (but would be acceptable in narayana-jta uber jar).

                                Agreed. I've updated the doc. Same applies for the Context too.

                                 

                                It still a good question about when you can actually execute the test phase as you need the container to have its version of Narayana replaced, currently we have to do these tests in scripts/hudson/narayana.sh after we have built the AS with Narayana in it. Maybe these cdi integration tests  should be moved to qa/ somewhere (on a related note, perhaps the XTS tests should exist under there too)? The AS for example has a "testsuite" hierarchy for integration tests.

                                I'm not sure what moving them to QA would buy us? The reason why AS has them there is that they can have maven run them after the 'build' module has been built and thus the AS is available. We still have to complete a Narayana and WildFly build before we can run our integration tests. So we would still need to drive these tests from the script.

                                • 13. Re: JTA 1.2 Implementation Work
                                  paul.robinson

                                  Tom,

                                  How are the out-of-scope beans garbage-collected?

                                   

                                  I would say that the afterCompletion seems to be the most intuitive solution rather than a new SPI. As you say, this should be compatible with the wording around not being able to rely on the context at this point.

                                  I agree, I've updated the document.

                                  • 14. Re: JTA 1.2 Implementation Work
                                    paul.robinson

                                    Tom,

                                    Re: What's the best way to enable this feature in WildFly?

                                    The Context needs to be enabled via a CDI Extension. I'm currently enabling this via a configuration file (javax.enterprise.inject.spi.Extension) placed in the META-INF of the application. I think this is the way to do this for applications that run in a simple CDI container. However, we can remove this burden for applications deployed to WildFly. It's quite simple to enable this Extension in a DeploymentProcessor of the transactions SubSystem. However, we would need to either, enable it for all deployments, or just those that use @TransactionScoped (I don't yet know how to detect this).

                                     

                                     

                                     

                                    Does enabling the extension when an application does not use it add overhead to the application? Maybe provide a way to disable it rather than enable it if so?

                                    I'm not sure. I'll check with Stuart Douglas when I ask him about activation.

                                    1 2 Previous Next