Browse Source

DATACMNS-715 - Introduced support for JTA 2.1 @Transactional.

Refreshed the copy of AnnotationTransactionAttributeSource to pull in the JTA 2.1 support introduced in the Spring Framework class. Opened up a ticket [0] to improve Spring Framework to eventually be able to ditch the copies to prevent such scenarios in the future (the missing feature introduced that is).
1.10.x
Oliver Gierke 11 years ago
parent
commit
164e64bb15
  1. 7
      pom.xml
  2. 75
      src/main/java/org/springframework/data/repository/core/support/TransactionalRepositoryProxyPostProcessor.java
  3. 31
      src/test/java/org/springframework/data/repository/core/support/TransactionRepositoryProxyPostProcessorUnitTests.java

7
pom.xml

@ -190,6 +190,13 @@ @@ -190,6 +190,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

75
src/main/java/org/springframework/data/repository/core/support/TransactionalRepositoryProxyPostProcessor.java

@ -34,6 +34,7 @@ import org.springframework.core.BridgeMethodResolver; @@ -34,6 +34,7 @@ import org.springframework.core.BridgeMethodResolver;
import org.springframework.dao.support.PersistenceExceptionTranslationInterceptor;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.transaction.annotation.Ejb3TransactionAnnotationParser;
import org.springframework.transaction.annotation.JtaTransactionAnnotationParser;
import org.springframework.transaction.annotation.SpringTransactionAnnotationParser;
import org.springframework.transaction.annotation.TransactionAnnotationParser;
import org.springframework.transaction.annotation.Transactional;
@ -110,10 +111,10 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr @@ -110,10 +111,10 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
* working with transaction metadata in JDK 1.5+ annotation format.
* <p>
* This class reads Spring's JDK 1.5+ {@link Transactional} annotation and exposes corresponding transaction
* attributes to Spring's transaction infrastructure. Also supports EJB3's {@link javax.ejb.TransactionAttribute}
* annotation (if present). This class may also serve as base class for a custom TransactionAttributeSource, or get
* customized through {@link TransactionAnnotationParser} strategies.
*
* attributes to Spring's transaction infrastructure. Also supports JTA 1.2's and EJB3's
* {@link javax.ejb.TransactionAttribute} annotation (if present). This class may also serve as base class for a
* custom TransactionAttributeSource, or get customized through {@link TransactionAnnotationParser} strategies.
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @since 1.2
@ -124,43 +125,50 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr @@ -124,43 +125,50 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
* @see org.springframework.transaction.interceptor.TransactionInterceptor#setTransactionAttributeSource
* @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean#setTransactionAttributeSource
*/
static class CustomAnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements
Serializable {
@SuppressWarnings("serial")
static class CustomAnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
implements Serializable {
private static final boolean jta12Present = ClassUtils.isPresent("javax.transaction.Transactional",
CustomAnnotationTransactionAttributeSource.class.getClassLoader());
private static final long serialVersionUID = 4841944452113159864L;
private static final boolean ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute",
CustomAnnotationTransactionAttributeSource.class.getClassLoader());
private final boolean publicMethodsOnly;
private final Set<TransactionAnnotationParser> annotationParsers;
/**
* Create a default AnnotationTransactionAttributeSource, supporting public methods that carry the
* <code>Transactional</code> annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
* Create a default CustomAnnotationTransactionAttributeSource, supporting public methods that carry the
* {@code Transactional} annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
*/
public CustomAnnotationTransactionAttributeSource() {
this(true);
}
/**
* Create a custom AnnotationTransactionAttributeSource, supporting public methods that carry the
* <code>Transactional</code> annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
* Create a custom CustomAnnotationTransactionAttributeSource, supporting public methods that carry the
* {@code Transactional} annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
*
* @param publicMethodsOnly whether to support public methods that carry the <code>Transactional</code> annotation
* only (typically for use with proxy-based AOP), or protected/private methods as well (typically used with
* @param publicMethodsOnly whether to support public methods that carry the {@code Transactional} annotation only
* (typically for use with proxy-based AOP), or protected/private methods as well (typically used with
* AspectJ class weaving)
*/
public CustomAnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2);
this.annotationParsers.add(new SpringTransactionAnnotationParser());
if (jta12Present) {
this.annotationParsers.add(new JtaTransactionAnnotationParser());
}
if (ejb3Present) {
this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
}
}
/**
* Create a custom AnnotationTransactionAttributeSource.
* Create a custom CustomAnnotationTransactionAttributeSource.
*
* @param annotationParser the TransactionAnnotationParser to use
*/
@ -171,7 +179,21 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr @@ -171,7 +179,21 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
}
/**
* Create a custom AnnotationTransactionAttributeSource.
* Create a custom CustomAnnotationTransactionAttributeSource.
*
* @param annotationParsers the TransactionAnnotationParsers to use
*/
public CustomAnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) {
this.publicMethodsOnly = true;
Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
Set<TransactionAnnotationParser> parsers = new LinkedHashSet<TransactionAnnotationParser>(
annotationParsers.length);
Collections.addAll(parsers, annotationParsers);
this.annotationParsers = parsers;
}
/**
* Create a custom CustomAnnotationTransactionAttributeSource.
*
* @param annotationParsers the TransactionAnnotationParsers to use
*/
@ -195,13 +217,12 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr @@ -195,13 +217,12 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
* Determine the transaction attribute for the given method or class.
* <p>
* This implementation delegates to configured {@link TransactionAnnotationParser TransactionAnnotationParsers} for
* parsing known annotations into Spring's metadata attribute class. Returns <code>null</code> if it's not
* transactional.
* parsing known annotations into Spring's metadata attribute class. Returns {@code null} if it's not transactional.
* <p>
* Can be overridden to support custom annotations that carry transaction metadata.
*
* @param ae the annotated method or class
* @return TransactionAttribute the configured transaction attribute, or <code>null</code> if none was found
* @return TransactionAttribute the configured transaction attribute, or {@code null} if none was found
*/
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
@ -220,6 +241,24 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr @@ -220,6 +241,24 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
protected boolean allowPublicMethodsOnly() {
return this.publicMethodsOnly;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CustomAnnotationTransactionAttributeSource)) {
return false;
}
CustomAnnotationTransactionAttributeSource otherTas = (CustomAnnotationTransactionAttributeSource) other;
return (this.annotationParsers.equals(otherTas.annotationParsers)
&& this.publicMethodsOnly == otherTas.publicMethodsOnly);
}
@Override
public int hashCode() {
return this.annotationParsers.hashCode();
}
}
/**

31
src/test/java/org/springframework/data/repository/core/support/TransactionRepositoryProxyPostProcessorUnitTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2008-2014 the original author or authors.
* Copyright 2008-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package org.springframework.data.repository.core.support;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
@ -24,11 +25,11 @@ import java.lang.reflect.Method; @@ -24,11 +25,11 @@ import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.ListableBeanFactory;
@ -38,6 +39,7 @@ import org.springframework.data.repository.core.RepositoryInformation; @@ -38,6 +39,7 @@ import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor.CustomAnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
@ -57,8 +59,8 @@ public class TransactionRepositoryProxyPostProcessorUnitTests { @@ -57,8 +59,8 @@ public class TransactionRepositoryProxyPostProcessorUnitTests {
Map<String, PersistenceExceptionTranslator> beans = new HashMap<String, PersistenceExceptionTranslator>();
beans.put("foo", mock(PersistenceExceptionTranslator.class));
when(beanFactory.getBeansOfType(eq(PersistenceExceptionTranslator.class), anyBoolean(), anyBoolean())).thenReturn(
beans);
when(beanFactory.getBeansOfType(eq(PersistenceExceptionTranslator.class), anyBoolean(), anyBoolean()))
.thenReturn(beans);
}
@Test(expected = IllegalArgumentException.class)
@ -78,7 +80,7 @@ public class TransactionRepositoryProxyPostProcessorUnitTests { @@ -78,7 +80,7 @@ public class TransactionRepositoryProxyPostProcessorUnitTests {
true);
postProcessor.postProcess(proxyFactory, repositoryInformation);
verify(proxyFactory).addAdvice(isA(TransactionInterceptor.class));
verify(proxyFactory).addAdvice(Mockito.any(TransactionInterceptor.class));
}
/**
@ -97,6 +99,20 @@ public class TransactionRepositoryProxyPostProcessorUnitTests { @@ -97,6 +99,20 @@ public class TransactionRepositoryProxyPostProcessorUnitTests {
assertTransactionAttributeFor(SampleImplementationWithClassAnnotation.class);
}
/**
* @see DATACMNS-732
*/
@Test
public void considersJtaTransactional() throws Exception {
Method method = SampleRepository.class.getMethod("methodWithJtaOneDotTwoAtTransactional");
TransactionAttributeSource attributeSource = new CustomAnnotationTransactionAttributeSource();
TransactionAttribute attribute = attributeSource.getTransactionAttribute(method, SampleRepository.class);
assertThat(attribute, is(notNullValue()));
}
private void assertTransactionAttributeFor(Class<?> implementationClass) throws Exception {
Method repositorySaveMethod = SampleRepository.class.getMethod("save", Sample.class);
@ -110,7 +126,7 @@ public class TransactionRepositoryProxyPostProcessorUnitTests { @@ -110,7 +126,7 @@ public class TransactionRepositoryProxyPostProcessorUnitTests {
TransactionAttribute attribute = attributeSource.getTransactionAttribute(repositorySaveMethod,
SampleImplementation.class);
assertThat(attribute, Matchers.is(Matchers.notNullValue()));
assertThat(attribute, is(notNullValue()));
}
static class Sample {}
@ -118,6 +134,9 @@ public class TransactionRepositoryProxyPostProcessorUnitTests { @@ -118,6 +134,9 @@ public class TransactionRepositoryProxyPostProcessorUnitTests {
interface SampleRepository extends Repository<Sample, Serializable> {
Sample save(Sample object);
@javax.transaction.Transactional
void methodWithJtaOneDotTwoAtTransactional();
}
static class SampleImplementation<T> {

Loading…
Cancel
Save