org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'

Spring JIRA | Chris Beams | 9 years ago
  1. 0

    Disclaimer: This may actually be an AspectJ bug. I can't tell at this point. The problem behavior I'm seeing is a bit hard to describe in prose; the best bet is to take a look at the code, beans xml and attached log output. Essentially, it looks like AspectJ pointcut expressions aren't recognizing methods that override method declarations that take generic parameters. For example, if I have a class MySet that implements Set<String>, an AspectJ pointcut expression of "execution(* MySet.*(..))" will NOT properly match MySet.add(String str). Again, this doesn't express very well in prose, so take a look at the code (I've made it as concise as possible) and see what you can make of it. I do have a workaround, which is detailed at the bottom of the issue. If necessary, I'd be happy to upload a complete testcase. If there are guidelines for testcase submission, please comment on this issue with a link to that documentation and I'll follow it. Consider the following interfaces and implementation: public interface Repository<T> extends Set<T> {} public interface ProductRepository extends Repository<Product> { Set<Product> findByCategory(String category); } public class JdoProductRepositoryImpl extends AbstractSet<Product> implements ProductRepository { private final PersistenceManagerFactory pmf; public JdoProductRepositoryImpl(PersistenceManagerFactory pmf) { this.pmf = pmf; } @Override public boolean add(Product product) { PersistenceManager pm = pmf.getPersistenceManager(); boolean isNew = !(JDOHelper.isPersistent(product) || JDOHelper.isDetached(product)); pm.makePersistent(product); return isNew; } // clear(), size(), iterator(), findByCategory() methods omitted for brevity } beans.xml: <beans> <bean id="pmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean" p:config-location="classpath:jpox.properties"/> <bean id="pmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy" p:target-persistence-manager-factory-ref="pmf" p:allow-create="true"/> <bean id="productRepos" class="demo.product.JdoProductRepositoryImpl"> <constructor-arg ref="pmfProxy"/> </bean> <bean id="txnManager" class="org.springframework.orm.jdo.JdoTransactionManager" p:persistence-manager-factory-ref="pmf"/> <tx:advice id="txnAdvice" transaction-manager="txnManager"> <tx:attributes> <tx:method name="clear" propagation="REQUIRED" read-only="true"/> <tx:method name="size" propagation="REQUIRED" read-only="true"/> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="add" propagation="REQUIRED" read-only="false"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txnAdvice" pointcut="execution(* demo.product.Repository+.*(..))"/> </aop:config> </beans> main method: public static void main(String... args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); ProductRepository productRepos = (ProductRepository) ctx.getBean("productRepos"); int size; productRepos.clear(); size = productRepos.size(); logger.info("size before add: " + size); assert size == 0; Product product = new Product("foo"); productRepos.add(product); // <-- throws here with the stack trace below size = productRepos.size(); logger.info("size after add: " + size); assert size == 1; logger.info("products with category 'foo': " + productRepos.findByCategory("foo").size()); logger.info("products with category 'bar': " + productRepos.findByCategory("bar").size()); } Stack trace: Exception in thread "main" org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true' at org.jpox.AbstractPersistenceManager.assertWritable(AbstractPersistenceManager.java:1959) at org.jpox.AbstractPersistenceManager.makePersistent(AbstractPersistenceManager.java:607) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy$TransactionAwareInvocationHandler.invoke(TransactionAwarePersistenceManagerFactoryProxy.java:212) at $Proxy2.makePersistent(Unknown Source) at demo.product.JdoProductRepositoryImpl.add(JdoProductRepositoryImpl.java:38) at demo.product.JdoProductRepositoryImpl.add(JdoProductRepositoryImpl.java:1) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:296) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:198) at $Proxy1.add(Unknown Source) at demo.app.Main.main(Main.java:33) So, as you'll see in the attached log output, a transaction is opened and closed just fine for the calls to clear() and to size(), but not for the call to add(product). This appears to be because add(product) is not getting advised by AspectJ whatsoever. I have found a simple workaround (that makes this issue feel all the more like a bug to me): If I explicitly declare an add(Product) method in the ProductRepository interface as follows, the call to add(product) works just fine (i.e.: a transaction is opened and closed appropriately): public interface ProductRepository extends Repository<Product> { void add(Product product); Set<Product> findByCategory(String category); } See the log.workaround.txt file for full output when this fix is in place. I've also attached my jpox.properties file for further clarity. Hopefully this is just an error on my part in understanding AspectJ pointcut syntax (or something else trivial). Again, I'd be happy to put together a more complete testcase if that helps, and/or answer any additional questions.

    Spring JIRA | 9 years ago | Chris Beams
    org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'
  2. 0

    Disclaimer: This may actually be an AspectJ bug. I can't tell at this point. The problem behavior I'm seeing is a bit hard to describe in prose; the best bet is to take a look at the code, beans xml and attached log output. Essentially, it looks like AspectJ pointcut expressions aren't recognizing methods that override method declarations that take generic parameters. For example, if I have a class MySet that implements Set<String>, an AspectJ pointcut expression of "execution(* MySet.*(..))" will NOT properly match MySet.add(String str). Again, this doesn't express very well in prose, so take a look at the code (I've made it as concise as possible) and see what you can make of it. I do have a workaround, which is detailed at the bottom of the issue. If necessary, I'd be happy to upload a complete testcase. If there are guidelines for testcase submission, please comment on this issue with a link to that documentation and I'll follow it. Consider the following interfaces and implementation: public interface Repository<T> extends Set<T> {} public interface ProductRepository extends Repository<Product> { Set<Product> findByCategory(String category); } public class JdoProductRepositoryImpl extends AbstractSet<Product> implements ProductRepository { private final PersistenceManagerFactory pmf; public JdoProductRepositoryImpl(PersistenceManagerFactory pmf) { this.pmf = pmf; } @Override public boolean add(Product product) { PersistenceManager pm = pmf.getPersistenceManager(); boolean isNew = !(JDOHelper.isPersistent(product) || JDOHelper.isDetached(product)); pm.makePersistent(product); return isNew; } // clear(), size(), iterator(), findByCategory() methods omitted for brevity } beans.xml: <beans> <bean id="pmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean" p:config-location="classpath:jpox.properties"/> <bean id="pmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy" p:target-persistence-manager-factory-ref="pmf" p:allow-create="true"/> <bean id="productRepos" class="demo.product.JdoProductRepositoryImpl"> <constructor-arg ref="pmfProxy"/> </bean> <bean id="txnManager" class="org.springframework.orm.jdo.JdoTransactionManager" p:persistence-manager-factory-ref="pmf"/> <tx:advice id="txnAdvice" transaction-manager="txnManager"> <tx:attributes> <tx:method name="clear" propagation="REQUIRED" read-only="true"/> <tx:method name="size" propagation="REQUIRED" read-only="true"/> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="add" propagation="REQUIRED" read-only="false"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txnAdvice" pointcut="execution(* demo.product.Repository+.*(..))"/> </aop:config> </beans> main method: public static void main(String... args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); ProductRepository productRepos = (ProductRepository) ctx.getBean("productRepos"); int size; productRepos.clear(); size = productRepos.size(); logger.info("size before add: " + size); assert size == 0; Product product = new Product("foo"); productRepos.add(product); // <-- throws here with the stack trace below size = productRepos.size(); logger.info("size after add: " + size); assert size == 1; logger.info("products with category 'foo': " + productRepos.findByCategory("foo").size()); logger.info("products with category 'bar': " + productRepos.findByCategory("bar").size()); } Stack trace: Exception in thread "main" org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true' at org.jpox.AbstractPersistenceManager.assertWritable(AbstractPersistenceManager.java:1959) at org.jpox.AbstractPersistenceManager.makePersistent(AbstractPersistenceManager.java:607) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy$TransactionAwareInvocationHandler.invoke(TransactionAwarePersistenceManagerFactoryProxy.java:212) at $Proxy2.makePersistent(Unknown Source) at demo.product.JdoProductRepositoryImpl.add(JdoProductRepositoryImpl.java:38) at demo.product.JdoProductRepositoryImpl.add(JdoProductRepositoryImpl.java:1) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:296) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:198) at $Proxy1.add(Unknown Source) at demo.app.Main.main(Main.java:33) So, as you'll see in the attached log output, a transaction is opened and closed just fine for the calls to clear() and to size(), but not for the call to add(product). This appears to be because add(product) is not getting advised by AspectJ whatsoever. I have found a simple workaround (that makes this issue feel all the more like a bug to me): If I explicitly declare an add(Product) method in the ProductRepository interface as follows, the call to add(product) works just fine (i.e.: a transaction is opened and closed appropriately): public interface ProductRepository extends Repository<Product> { void add(Product product); Set<Product> findByCategory(String category); } See the log.workaround.txt file for full output when this fix is in place. I've also attached my jpox.properties file for further clarity. Hopefully this is just an error on my part in understanding AspectJ pointcut syntax (or something else trivial). Again, I'd be happy to put together a more complete testcase if that helps, and/or answer any additional questions.

    Spring JIRA | 9 years ago | Chris Beams
    org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'

    Root Cause Analysis

    1. org.jpox.jdo.exceptions.TransactionNotActiveException

      Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'

      at org.jpox.AbstractPersistenceManager.assertWritable()
    2. org.jpox
      AbstractPersistenceManager.makePersistent
      1. org.jpox.AbstractPersistenceManager.assertWritable(AbstractPersistenceManager.java:1959)
      2. org.jpox.AbstractPersistenceManager.makePersistent(AbstractPersistenceManager.java:607)
      2 frames
    3. Java RT
      Method.invoke
      1. sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      2. sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      3. sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      4. java.lang.reflect.Method.invoke(Method.java:585)
      4 frames
    4. Spring ORM
      TransactionAwarePersistenceManagerFactoryProxy$TransactionAwareInvocationHandler.invoke
      1. org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy$TransactionAwareInvocationHandler.invoke(TransactionAwarePersistenceManagerFactoryProxy.java:212)
      1 frame
    5. Unknown
      $Proxy2.makePersistent
      1. $Proxy2.makePersistent(Unknown Source)
      1 frame
    6. demo.product
      JdoProductRepositoryImpl.add
      1. demo.product.JdoProductRepositoryImpl.add(JdoProductRepositoryImpl.java:38)
      2. demo.product.JdoProductRepositoryImpl.add(JdoProductRepositoryImpl.java:1)
      2 frames
    7. Java RT
      Method.invoke
      1. sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      2. sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      3. sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      4. java.lang.reflect.Method.invoke(Method.java:585)
      4 frames
    8. Spring AOP
      JdkDynamicAopProxy.invoke
      1. org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:296)
      2. org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:198)
      2 frames
    9. Unknown
      $Proxy1.add
      1. $Proxy1.add(Unknown Source)
      1 frame
    10. demo.app
      Main.main
      1. demo.app.Main.main(Main.java:33)
      1 frame