Issue 1:
Chris Beams opened SPR-3879 and commented

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.

Affects: 2.0.6

Attachments:

jpox.properties (682 bytes)

log.txt (9.08 kB)

log.workaround.txt (18.78 kB)

Issue Links:

#8239 Spring AOP doesn't process generics correctly ("duplicates")

Issue 2:
Andrei Stefan opened SPR-3556 and commented

I made two tests:

one with AspectJ support;

one with Spring's AspectJ support.

The tests involve two interfaces: Base<T> and DerivedInterface<T> extends Base<T>.

A Before pointcut matching all methods from Base and DerivedInterface is defined: @Before("execution(* example.Base+.*(..))")

In AspectJ a test creating an instance of a class implementing DerivedInterface: DerivedInterface<String> obj = new DerivedString(); and then calling methods on both interfaces demonstrates advice being applied on both methods.

The same test in Spring getting a bean instance from ApplicationContext like DerivedInterface<String> bean = (DerivedInterface<String>) context.getBean("myBean"); and calling methods on both interfaces, the advice is not applied to methods receiving as parameter the generic type <T>.

Methods like method(<T>) don't get the advice applied, but method2() have the advice applied.

Affects: 2.0.5, 2.1 M3

Attachments:

SpringAspectJ.zip (1.86 kB)

Issue Links:

#8309 Spring AOP @Aspect pointcut ignores generic type params and matches to many join points ("is depended on by")

#8559 AspectJ pointcut expressions fail to match generically parameterized methods ("is duplicated by")