Add additional class conditions for JTA auto-configuration

Previously, JTA auto-configuration would fail with a variety of
ClassNotFoundExceptions and NoClassDefFoundErrors if it was used with
an “incomplete” classpath. This commit adds a number of classes to
@ConditionalOnClass annotations so that the auto-configuration backs
off gracefully in the absence of certain classes.

Specifically, the following now work as expected:

 - Deploying an app to a server with JTA available via JNDI when the
   app does not use transactions
 - Auto-configuration of Atomikos without JMS
 - Auto-configuration of Bitronix without JMS

Both XADataSourceAutoConfiguration and JndiDataSourceAutoConfiguration
have been updated to back off in the absence of spring-jdbc; a
dependency of DataSourceProperties which is used by both classes.

Error handling in AtomikosDependsOnBeanFactoryPostProcessor and
BitronixDependentBeanFactoryPostProcessor has been enhanced so that the
correct dependencies are established, even in the absence of JMS.

Fixes gh-1538
This commit is contained in:
Andy Wilkinson 2014-09-12 14:19:10 -05:00
parent 59ce634437
commit 6f9f335ea1
8 changed files with 41 additions and 19 deletions

View File

@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
/**
@ -38,7 +39,7 @@ import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
@Configuration
@AutoConfigureBefore({ XADataSourceAutoConfiguration.class,
DataSourceAutoConfiguration.class })
@ConditionalOnClass(DataSource.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnProperty(prefix = DataSourceProperties.PREFIX, name = "jndi-name")
@EnableConfigurationProperties(DataSourceProperties.class)
public class JndiDataSourceAutoConfiguration {
@ -50,4 +51,4 @@ public class JndiDataSourceAutoConfiguration {
return dataSourceLookup.getDataSource(properties.getJndiName());
}
}
}

View File

@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.XADataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -50,7 +51,8 @@ import org.springframework.util.StringUtils;
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@AutoConfigureAfter(JtaAutoConfiguration.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@ConditionalOnClass({ DataSource.class, TransactionManager.class })
@ConditionalOnClass({ DataSource.class, TransactionManager.class,
EmbeddedDatabaseType.class })
@ConditionalOnBean(XADataSourceWrapper.class)
@ConditionalOnMissingBean(DataSource.class)
public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {

View File

@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jta;
import java.io.File;
import java.util.Properties;
import javax.jms.Message;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
@ -51,7 +52,7 @@ import com.atomikos.icatch.jta.UserTransactionManager;
* @since 1.2.0
*/
@Configuration
@ConditionalOnClass(UserTransactionManager.class)
@ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
class AtomikosJtaConfiguration {
@ -99,12 +100,6 @@ class AtomikosJtaConfiguration {
return new AtomikosXADataSourceWrapper();
}
@Bean
@ConditionalOnMissingBean
public XAConnectionFactoryWrapper xaConnectionFactoryWrapper() {
return new AtomikosXAConnectionFactoryWrapper();
}
@Bean
@ConditionalOnMissingBean
public static AtomikosDependsOnBeanFactoryPostProcessor atomikosDependsOnBeanFactoryPostProcessor() {
@ -117,4 +112,16 @@ class AtomikosJtaConfiguration {
return new JtaTransactionManager(userTransaction, transactionManager);
}
@Configuration
@ConditionalOnClass(Message.class)
static class AtomikosJtaJmsConfiguration {
@Bean
@ConditionalOnMissingBean
public XAConnectionFactoryWrapper xaConnectionFactoryWrapper() {
return new AtomikosXAConnectionFactoryWrapper();
}
}
}

View File

@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jta;
import java.io.File;
import javax.jms.Message;
import javax.transaction.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
@ -47,7 +48,7 @@ import bitronix.tm.jndi.BitronixContext;
* @since 1.2.0
*/
@Configuration
@ConditionalOnClass(BitronixContext.class)
@ConditionalOnClass({ JtaTransactionManager.class, BitronixContext.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
class BitronixJtaConfiguration {
@ -89,12 +90,6 @@ class BitronixJtaConfiguration {
return new BitronixXADataSourceWrapper();
}
@Bean
@ConditionalOnMissingBean
public XAConnectionFactoryWrapper xaConnectionFactoryWrapper() {
return new BitronixXAConnectionFactoryWrapper();
}
@Bean
@ConditionalOnMissingBean
public static BitronixDependentBeanFactoryPostProcessor atomikosDependsOnBeanFactoryPostProcessor() {
@ -106,4 +101,13 @@ class BitronixJtaConfiguration {
return new JtaTransactionManager(transactionManager);
}
@ConditionalOnClass(Message.class)
static class BitronixJtaJmsConfiguration {
@Bean
@ConditionalOnMissingBean
public XAConnectionFactoryWrapper xaConnectionFactoryWrapper() {
return new BitronixXAConnectionFactoryWrapper();
}
}
}

View File

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.jta;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@ -24,12 +25,13 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
/**
* JTA Configuration for a JDNI managed {@link JtaTransactionManager}.
* JTA Configuration for a JNDI-managed {@link JtaTransactionManager}.
*
* @author Phillip Webb
* @since 1.2.0
*/
@Configuration
@ConditionalOnClass(JtaTransactionManager.class)
@ConditionalOnJndi({ JtaTransactionManager.DEFAULT_USER_TRANSACTION_NAME,
"java:comp/TransactionManager", "java:appserver/TransactionManager",
"java:pm/TransactionManager", "java:/TransactionManager" })

View File

@ -61,7 +61,7 @@ public class JtaAutoConfigurationTests {
}
@Test
public void customPatformTransactionManager() throws Exception {
public void customPlatformTransactionManager() throws Exception {
this.context = new AnnotationConfigApplicationContext(
CustomTransactionManagerConfig.class, JtaAutoConfiguration.class);
this.thrown.expect(NoSuchBeanDefinitionException.class);

View File

@ -94,6 +94,9 @@ public class AtomikosDependsOnBeanFactoryPostProcessor implements
catch (ClassNotFoundException ex) {
// Ignore
}
catch (NoClassDefFoundError ex) {
// Ignore
}
return NO_BEANS;
}

View File

@ -70,6 +70,9 @@ public class BitronixDependentBeanFactoryPostProcessor implements
catch (ClassNotFoundException ex) {
// Ignore
}
catch (NoClassDefFoundError ex) {
// Ignore
}
return NO_BEANS;
}