1 Reply Latest reply on Apr 21, 2012 12:27 PM by Stephen Coy

    Transactions not starting on JSF @ViewScoped @Stateless bean

    Karsten Wutzke Expert

      Hello,

       

      this is probably not the perfect forum for this, but I'll try anyway.

       

      I have a JSF 2 @ViewScoped based webapp, that I cannot get the transactions to go with correctly, or rather: they don't start at all.

       

      I'm using Java EE 6's CDI and EJB3, too.

       

      Here's the main bean:

       

      {code}    import javax.faces.bean.ManagedBean;

          import javax.faces.bean.ViewScoped;

          import javax.inject.Inject;

          ...

       

          @ManagedBean

          @ViewScoped

          @Stateless

          public class PqManager implements Serializable

          {

              private List<PqListItem> pqItems;

             

              @Inject

              private PqService pqService;

       

              public List<PqListItem> getPqItems()

              {

                  if ( pqItems == null )

                  {

                      pqItems = pqService.findActivePqs();

                  }

                 

                  return pqItems;

              }

       

              ...

          }{code}

      The view-scoped bean is used from a JSF page to display a simple list in a datatable. It was made view-scoped because it has AJAX-based operations to add items, remove items, and to sort them via RichFaces (filtering).

       

      I added @Stateless for every method invocation to start a transaction (or create a new one if none exists, the default is TransactionAttributeType.REQUIRED). This idea was taken from the book 'Core JavaServer Faces, 3rd ed.', however I haven't found any examples that would match my own.

       

      Injected PqService class (it doesn't make a difference to use @EJB instead):

      {code}    @Stateless

          public class PqService extends JpaCrudService

          {

              ...

             

              public List<PqListItem> findActivePqs()

              {

                  return em.createQuery("SELECT NEW ... whatever not interesting here... WHERE pq.workflow = '" + Workflow.ACTIVE + "' GROUP BY pq.id", PqListItem.class).getResultList();

              }

             

              ...

          }{code}

      JpaCrudService (basically taken from Adam Bien's example http://www.adam-bien.com/roller/abien/entry/generic_crud_service_aka_dao):

      {code}    //@Stateless

          //@Local(CrudService.class)

          @TransactionAttribute(TransactionAttributeType.MANDATORY)

          public abstract class JpaCrudService implements CrudService

          {

              @PersistenceContext(unitName = "PqGeneratorPu")

              protected EntityManager em;

             

              @Override

              public <T> T create(T t)

              {

                  em.persist(t);

                  em.flush();

                  em.refresh(t);

                 

                  return t;

              }

       

              ...

          }{code}

      The only difference is that I subclass `JpaCrudService` because I don't like queries stored in/at the entities. So I omitted the @Local annotation (correct me if that's wrong). @Stateless isn't inherited AFAIK and I only inject the subclasses so I also commented that one out.

       

      That said, the bean is then accessed from a JSF page:

      {code}      <rich:dataTable value="#{pqManager.pqItems}"

                            var="pq">

              <f:facet name="header">

                <h:outputText value="Active" />

              </f:facet>

              ...{code}

      However, when loading the page, I get an exception:

      {code}    javax.ejb.EJBTransactionRequiredException: Transaction is required for invocation: org.jboss.invocation.InterceptorContext@7a6c1c92

              at org.jboss.as.ejb3.tx.CMTTxInterceptor.mandatory(CMTTxInterceptor.java:255)

              at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:184)

              at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)

              at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)

              at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)

              at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59)

              at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)

              at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)

              at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)

              at org.jboss.as.ee.component.TCCLInterceptor.processInvocation(TCCLInterceptor.java:45)

              at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)

              at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)

              at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:165)

              at org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:173)

              at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)

              at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)

              at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:72)

              at de.company.webapp.service.PqService$$$view95.findActivePqsFor(Unknown Source)

              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

              at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

              at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

              at java.lang.reflect.Method.invoke(Unknown Source)

              at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:264)

              at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)

              at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)

              at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:260)

              at org.jboss.weld.bean.proxy.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:111)

              at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)

              at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:105)

              at de.company.webapp.service.PqService$Proxy$_$$_Weld$Proxy$.findActivePqs(PqService$Proxy$_$$_Weld$Proxy$.java)

              at de.company.webapp.facade.PqManager.getPqItems(PqManager.java:84)

              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

              at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

              at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

              at java.lang.reflect.Method.invoke(Unknown Source)

              .

              .

              .{code}

      It fails because the call pqService.findActivePqsFor() doesn't run in an existing transaction (TransactionAttributeType.MANDATORY, which is inherited AFAIK).

       

      Note, that the page is displayed correctly without using transactions by deleting the TransactionAttributeType.MANDATORY on the JpaCrudService and using an extended entity manager, but this was just for testing purposes.

       

      But why isn't this working? Why isn't the transaction started here? Is there anything with the JSF @ViewScoped bean? Incompatible?

       

      How do you repair this?

       

      PS: I'm using JBoss AS 7.1.1.