Create spring-boot-tx module
This commit is contained in:
committed by
Phillip Webb
parent
524db779e5
commit
5360ef8321
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.transaction.ConfigurableTransactionManager;
|
||||
import org.springframework.transaction.TransactionExecutionListener;
|
||||
|
||||
/**
|
||||
* {@link TransactionManagerCustomizer} that adds {@link TransactionExecutionListener
|
||||
* execution listeners} to any transaction manager that is
|
||||
* {@link ConfigurableTransactionManager configurable}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ExecutionListenersTransactionManagerCustomizer
|
||||
implements TransactionManagerCustomizer<ConfigurableTransactionManager> {
|
||||
|
||||
private final List<TransactionExecutionListener> listeners;
|
||||
|
||||
ExecutionListenersTransactionManagerCustomizer(List<TransactionExecutionListener> listeners) {
|
||||
this.listeners = listeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableTransactionManager transactionManager) {
|
||||
this.listeners.forEach(transactionManager::addListener);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import org.springframework.boot.LazyInitializationExcludeFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.transaction.aspectj.AbstractTransactionAspect;
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for Spring transaction.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(PlatformTransactionManager.class)
|
||||
public class TransactionAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnSingleCandidate(ReactiveTransactionManager.class)
|
||||
public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {
|
||||
return TransactionalOperator.create(transactionManager);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
|
||||
public static class TransactionTemplateConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TransactionOperations.class)
|
||||
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
|
||||
return new TransactionTemplate(transactionManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(TransactionManager.class)
|
||||
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
|
||||
public static class EnableTransactionManagementConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableTransactionManagement(proxyTargetClass = false)
|
||||
@ConditionalOnBooleanProperty(name = "spring.aop.proxy-target-class", havingValue = false)
|
||||
public static class JdkDynamicAutoProxyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableTransactionManagement(proxyTargetClass = true)
|
||||
@ConditionalOnBooleanProperty(name = "spring.aop.proxy-target-class", matchIfMissing = true)
|
||||
public static class CglibAutoProxyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(AbstractTransactionAspect.class)
|
||||
static class AspectJTransactionManagementConfiguration {
|
||||
|
||||
@Bean
|
||||
static LazyInitializationExcludeFilter eagerTransactionAspect() {
|
||||
return LazyInitializationExcludeFilter.forBeanTypes(AbstractTransactionAspect.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionExecutionListener;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
|
||||
/**
|
||||
* Auto-configuration for the customization of a {@link TransactionManager}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConditionalOnClass(PlatformTransactionManager.class)
|
||||
@AutoConfiguration(before = TransactionAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(TransactionProperties.class)
|
||||
public class TransactionManagerCustomizationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TransactionManagerCustomizers platformTransactionManagerCustomizers(
|
||||
ObjectProvider<TransactionManagerCustomizer<?>> customizers) {
|
||||
return TransactionManagerCustomizers.of(customizers.orderedStream().toList());
|
||||
}
|
||||
|
||||
@Bean
|
||||
ExecutionListenersTransactionManagerCustomizer transactionExecutionListeners(
|
||||
ObjectProvider<TransactionExecutionListener> listeners) {
|
||||
return new ExecutionListenersTransactionManagerCustomizer(listeners.orderedStream().toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize
|
||||
* {@link TransactionManager TransactionManagers} while retaining default
|
||||
* auto-configuration.
|
||||
*
|
||||
* @param <T> the transaction manager type
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface TransactionManagerCustomizer<T extends TransactionManager> {
|
||||
|
||||
/**
|
||||
* Customize the given transaction manager.
|
||||
* @param transactionManager the transaction manager to customize
|
||||
*/
|
||||
void customize(T transactionManager);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012-2024 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
|
||||
/**
|
||||
* A collection of {@link TransactionManagerCustomizer TransactionManagerCustomizers}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public final class TransactionManagerCustomizers {
|
||||
|
||||
private final List<? extends TransactionManagerCustomizer<?>> customizers;
|
||||
|
||||
private TransactionManagerCustomizers(List<? extends TransactionManagerCustomizer<?>> customizers) {
|
||||
this.customizers = customizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the given {@code transactionManager}.
|
||||
* @param transactionManager the transaction manager to customize
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void customize(TransactionManager transactionManager) {
|
||||
LambdaSafe.callbacks(TransactionManagerCustomizer.class, this.customizers, transactionManager)
|
||||
.withLogger(TransactionManagerCustomizers.class)
|
||||
.invoke((customizer) -> customizer.customize(transactionManager));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code TransactionManagerCustomizers} instance containing the given
|
||||
* {@code customizers}.
|
||||
* @param customizers the customizers
|
||||
* @return the new instance
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public static TransactionManagerCustomizers of(Collection<? extends TransactionManagerCustomizer<?>> customizers) {
|
||||
return new TransactionManagerCustomizers(
|
||||
(customizers != null) ? new ArrayList<>(customizers) : Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Configuration properties that can be applied to an
|
||||
* {@link AbstractPlatformTransactionManager}.
|
||||
*
|
||||
* @author Kazuki Shimizu
|
||||
* @author Phillip Webb
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.transaction")
|
||||
public class TransactionProperties implements TransactionManagerCustomizer<AbstractPlatformTransactionManager> {
|
||||
|
||||
/**
|
||||
* Default transaction timeout. If a duration suffix is not specified, seconds will be
|
||||
* used.
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration defaultTimeout;
|
||||
|
||||
/**
|
||||
* Whether to roll back on commit failures.
|
||||
*/
|
||||
private Boolean rollbackOnCommitFailure;
|
||||
|
||||
public Duration getDefaultTimeout() {
|
||||
return this.defaultTimeout;
|
||||
}
|
||||
|
||||
public void setDefaultTimeout(Duration defaultTimeout) {
|
||||
this.defaultTimeout = defaultTimeout;
|
||||
}
|
||||
|
||||
public Boolean getRollbackOnCommitFailure() {
|
||||
return this.rollbackOnCommitFailure;
|
||||
}
|
||||
|
||||
public void setRollbackOnCommitFailure(Boolean rollbackOnCommitFailure) {
|
||||
this.rollbackOnCommitFailure = rollbackOnCommitFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(AbstractPlatformTransactionManager transactionManager) {
|
||||
if (this.defaultTimeout != null) {
|
||||
transactionManager.setDefaultTimeout((int) this.defaultTimeout.getSeconds());
|
||||
}
|
||||
if (this.rollbackOnCommitFailure != null) {
|
||||
transactionManager.setRollbackOnCommitFailure(this.rollbackOnCommitFailure);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for transaction support.
|
||||
*/
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-2024 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.jta.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizers;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
|
||||
/**
|
||||
* JTA Configuration for a JNDI-managed {@link JtaTransactionManager}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Kazuki Shimizu
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(JtaTransactionManager.class)
|
||||
@ConditionalOnJndi({ JtaTransactionManager.DEFAULT_USER_TRANSACTION_NAME, "java:comp/TransactionManager",
|
||||
"java:appserver/TransactionManager", "java:pm/TransactionManager", "java:/TransactionManager" })
|
||||
@ConditionalOnMissingBean(org.springframework.transaction.TransactionManager.class)
|
||||
class JndiJtaConfiguration {
|
||||
|
||||
@Bean
|
||||
JtaTransactionManager transactionManager(
|
||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
|
||||
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(jtaTransactionManager));
|
||||
return jtaTransactionManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.jta.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for JTA.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Phillip Webb
|
||||
* @author Nishant Raut
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(before = { TransactionAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class })
|
||||
@ConditionalOnClass(jakarta.transaction.Transaction.class)
|
||||
@ConditionalOnBooleanProperty(name = "spring.jta.enabled", matchIfMissing = true)
|
||||
@Import(JndiJtaConfiguration.class)
|
||||
public class JtaAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for JTA.
|
||||
*/
|
||||
package org.springframework.boot.transaction.jta.autoconfigure;
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration
|
||||
org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration
|
||||
org.springframework.boot.transaction.jta.autoconfigure.JtaAutoConfiguration
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.transaction.ConfigurableTransactionManager;
|
||||
import org.springframework.transaction.TransactionExecutionListener;
|
||||
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ExecutionListenersTransactionManagerCustomizer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ExecutionListenersTransactionManagerCustomizerTests {
|
||||
|
||||
@Test
|
||||
void whenTransactionManagerIsCustomizedThenExecutionListenersAreAddedToIt() {
|
||||
TransactionExecutionListener listener1 = mock(TransactionExecutionListener.class);
|
||||
TransactionExecutionListener listener2 = mock(TransactionExecutionListener.class);
|
||||
ConfigurableTransactionManager transactionManager = mock(ConfigurableTransactionManager.class);
|
||||
new ExecutionListenersTransactionManagerCustomizer(List.of(listener1, listener2)).customize(transactionManager);
|
||||
then(transactionManager).should().addListener(listener1);
|
||||
then(transactionManager).should().addListener(listener2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.LazyInitializationExcludeFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.aspectj.AbstractTransactionAspect;
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link TransactionAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TransactionAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(TransactionAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void whenThereIsNoPlatformTransactionManagerNoTransactionTemplateIsAutoConfigured() {
|
||||
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(TransactionTemplate.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenThereIsASinglePlatformTransactionManagerATransactionTemplateIsAutoConfigured() {
|
||||
this.contextRunner.withUserConfiguration(SinglePlatformTransactionManagerConfiguration.class).run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
TransactionTemplate transactionTemplate = context.getBean(TransactionTemplate.class);
|
||||
assertThat(transactionTemplate.getTransactionManager()).isSameAs(transactionManager);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenThereIsASingleReactiveTransactionManagerATransactionalOperatorIsAutoConfigured() {
|
||||
this.contextRunner.withUserConfiguration(SingleReactiveTransactionManagerConfiguration.class).run((context) -> {
|
||||
ReactiveTransactionManager transactionManager = context.getBean(ReactiveTransactionManager.class);
|
||||
TransactionalOperator transactionalOperator = context.getBean(TransactionalOperator.class);
|
||||
assertThat(transactionalOperator).extracting("transactionManager").isSameAs(transactionManager);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenThereAreBothReactiveAndPlatformTransactionManagersATemplateAndAnOperatorAreAutoConfigured() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(SinglePlatformTransactionManagerConfiguration.class,
|
||||
SingleReactiveTransactionManagerConfiguration.class)
|
||||
.withPropertyValues("spring.datasource.url:jdbc:h2:mem:" + UUID.randomUUID())
|
||||
.run((context) -> {
|
||||
PlatformTransactionManager platformTransactionManager = context
|
||||
.getBean(PlatformTransactionManager.class);
|
||||
TransactionTemplate transactionTemplate = context.getBean(TransactionTemplate.class);
|
||||
assertThat(transactionTemplate.getTransactionManager()).isSameAs(platformTransactionManager);
|
||||
ReactiveTransactionManager reactiveTransactionManager = context
|
||||
.getBean(ReactiveTransactionManager.class);
|
||||
TransactionalOperator transactionalOperator = context.getBean(TransactionalOperator.class);
|
||||
assertThat(transactionalOperator).extracting("transactionManager").isSameAs(reactiveTransactionManager);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenThereAreSeveralPlatformTransactionManagersNoTransactionTemplateIsAutoConfigured() {
|
||||
this.contextRunner.withUserConfiguration(SeveralPlatformTransactionManagersConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(TransactionTemplate.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenThereAreSeveralReactiveTransactionManagersNoTransactionOperatorIsAutoConfigured() {
|
||||
this.contextRunner.withUserConfiguration(SeveralReactiveTransactionManagersConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(TransactionalOperator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAUserProvidesATransactionTemplateTheAutoConfiguredTemplateBacksOff() {
|
||||
this.contextRunner.withUserConfiguration(CustomPlatformTransactionManagerConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(TransactionTemplate.class);
|
||||
assertThat(context.getBean("transactionTemplateFoo")).isInstanceOf(TransactionTemplate.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAUserProvidesATransactionalOperatorTheAutoConfiguredOperatorBacksOff() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(SingleReactiveTransactionManagerConfiguration.class,
|
||||
CustomTransactionalOperatorConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(TransactionalOperator.class);
|
||||
assertThat(context.getBean("customTransactionalOperator")).isInstanceOf(TransactionalOperator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionNotManagedWithNoTransactionManager() {
|
||||
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
|
||||
.run((context) -> assertThat(context.getBean(TransactionalService.class).isTransactionActive()).isFalse());
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionManagerUsesCglibByDefault() {
|
||||
this.contextRunner.withUserConfiguration(PlatformTransactionManagersConfiguration.class).run((context) -> {
|
||||
assertThat(context.getBean(AnotherServiceImpl.class).isTransactionActive()).isTrue();
|
||||
assertThat(context.getBeansOfType(TransactionalServiceImpl.class)).hasSize(1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionManagerCanBeConfiguredToJdkProxy() {
|
||||
this.contextRunner.withUserConfiguration(PlatformTransactionManagersConfiguration.class)
|
||||
.withPropertyValues("spring.aop.proxy-target-class=false")
|
||||
.run((context) -> {
|
||||
assertThat(context.getBean(AnotherService.class).isTransactionActive()).isTrue();
|
||||
assertThat(context).doesNotHaveBean(AnotherServiceImpl.class);
|
||||
assertThat(context).doesNotHaveBean(TransactionalServiceImpl.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void customEnableTransactionManagementTakesPrecedence() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomTransactionManagementConfiguration.class,
|
||||
PlatformTransactionManagersConfiguration.class)
|
||||
.withPropertyValues("spring.aop.proxy-target-class=true")
|
||||
.run((context) -> {
|
||||
assertThat(context.getBean(AnotherService.class).isTransactionActive()).isTrue();
|
||||
assertThat(context).doesNotHaveBean(AnotherServiceImpl.class);
|
||||
assertThat(context).doesNotHaveBean(TransactionalServiceImpl.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void excludesAbstractTransactionAspectFromLazyInit() {
|
||||
this.contextRunner.withUserConfiguration(AspectJTransactionManagementConfiguration.class).run((context) -> {
|
||||
LazyInitializationExcludeFilter filter = context.getBean(LazyInitializationExcludeFilter.class);
|
||||
assertThat(filter.isExcluded(null, null, AbstractTransactionAspect.class)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SinglePlatformTransactionManagerConfiguration {
|
||||
|
||||
@Bean
|
||||
PlatformTransactionManager transactionManager() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SingleReactiveTransactionManagerConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveTransactionManager reactiveTransactionManager() {
|
||||
return mock(ReactiveTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SeveralPlatformTransactionManagersConfiguration {
|
||||
|
||||
@Bean
|
||||
PlatformTransactionManager transactionManagerOne() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PlatformTransactionManager transactionManagerTwo() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SeveralReactiveTransactionManagersConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveTransactionManager reactiveTransactionManager1() {
|
||||
return mock(ReactiveTransactionManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveTransactionManager reactiveTransactionManager2() {
|
||||
return mock(ReactiveTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomPlatformTransactionManagerConfiguration {
|
||||
|
||||
@Bean
|
||||
TransactionTemplate transactionTemplateFoo(PlatformTransactionManager transactionManager) {
|
||||
return new TransactionTemplate(transactionManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PlatformTransactionManager transactionManagerFoo() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomTransactionalOperatorConfiguration {
|
||||
|
||||
@Bean
|
||||
TransactionalOperator customTransactionalOperator() {
|
||||
return mock(TransactionalOperator.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
TransactionalService transactionalService() {
|
||||
return new TransactionalServiceImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
AnotherServiceImpl anotherService() {
|
||||
return new AnotherServiceImpl();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(BaseConfiguration.class)
|
||||
static class PlatformTransactionManagersConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSourceTransactionManager transactionManager(DataSource dataSource) {
|
||||
return new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
return DataSourceBuilder.create()
|
||||
.driverClassName("org.hsqldb.jdbc.JDBCDriver")
|
||||
.url("jdbc:hsqldb:mem:tx")
|
||||
.username("sa")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableTransactionManagement(proxyTargetClass = false)
|
||||
static class CustomTransactionManagementConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
|
||||
static class AspectJTransactionManagementConfiguration {
|
||||
|
||||
}
|
||||
|
||||
interface TransactionalService {
|
||||
|
||||
@Transactional
|
||||
boolean isTransactionActive();
|
||||
|
||||
}
|
||||
|
||||
static class TransactionalServiceImpl implements TransactionalService {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionActive() {
|
||||
return TransactionSynchronizationManager.isActualTransactionActive();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface AnotherService {
|
||||
|
||||
boolean isTransactionActive();
|
||||
|
||||
}
|
||||
|
||||
static class AnotherServiceImpl implements AnotherService {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean isTransactionActive() {
|
||||
return TransactionSynchronizationManager.isActualTransactionActive();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012-2024 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TransactionManagerCustomizationAutoConfiguration}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class TransactionManagerCustomizationAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(TransactionManagerCustomizationAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void autoConfiguresTransactionManagerCustomizers() {
|
||||
this.contextRunner.run((context) -> {
|
||||
TransactionManagerCustomizers customizers = context.getBean(TransactionManagerCustomizers.class);
|
||||
assertThat(customizers).extracting("customizers")
|
||||
.asInstanceOf(InstanceOfAssertFactories.LIST)
|
||||
.hasSize(2)
|
||||
.hasAtLeastOneElementOfType(TransactionProperties.class)
|
||||
.hasAtLeastOneElementOfType(ExecutionListenersTransactionManagerCustomizer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguredTransactionManagerCustomizersBacksOff() {
|
||||
this.contextRunner.withUserConfiguration(CustomTransactionManagerCustomizersConfiguration.class)
|
||||
.run((context) -> {
|
||||
TransactionManagerCustomizers customizers = context.getBean(TransactionManagerCustomizers.class);
|
||||
assertThat(customizers).extracting("customizers")
|
||||
.asInstanceOf(InstanceOfAssertFactories.LIST)
|
||||
.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomTransactionManagerCustomizersConfiguration {
|
||||
|
||||
@Bean
|
||||
TransactionManagerCustomizers customTransactionManagerCustomizers() {
|
||||
return TransactionManagerCustomizers.of(Collections.<TransactionManagerCustomizer<?>>emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012-2024 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.autoconfigure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link TransactionManagerCustomizers}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TransactionManagerCustomizersTests {
|
||||
|
||||
@Test
|
||||
void customizeWithNullCustomizersShouldDoNothing() {
|
||||
TransactionManagerCustomizers.of(null).customize(mock(TransactionManager.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeShouldCheckGeneric() {
|
||||
List<TestCustomizer<?>> list = new ArrayList<>();
|
||||
list.add(new TestCustomizer<>());
|
||||
list.add(new TestJtaCustomizer());
|
||||
TransactionManagerCustomizers customizers = TransactionManagerCustomizers.of(list);
|
||||
customizers.customize(mock(PlatformTransactionManager.class));
|
||||
customizers.customize(mock(JtaTransactionManager.class));
|
||||
assertThat(list.get(0).getCount()).isEqualTo(2);
|
||||
assertThat(list.get(1).getCount()).isOne();
|
||||
}
|
||||
|
||||
static class TestCustomizer<T extends PlatformTransactionManager> implements TransactionManagerCustomizer<T> {
|
||||
|
||||
private int count;
|
||||
|
||||
@Override
|
||||
public void customize(T transactionManager) {
|
||||
this.count++;
|
||||
}
|
||||
|
||||
int getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestJtaCustomizer extends TestCustomizer<JtaTransactionManager> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.transaction.jta.autoconfigure;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import jakarta.transaction.TransactionManager;
|
||||
import jakarta.transaction.UserTransaction;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.osjava.sj.loader.JndiLoader;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.transaction.jta.UserTransactionAdapter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link JtaAutoConfiguration}.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Kazuki Shimizu
|
||||
* @author Nishant Raut
|
||||
*/
|
||||
@ClassPathExclusions("jetty-jndi-*.jar")
|
||||
class JtaAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@AfterEach
|
||||
void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ExtendWith(JndiExtension.class)
|
||||
@MethodSource("transactionManagerJndiEntries")
|
||||
void transactionManagerFromJndi(JndiEntry jndiEntry, InitialContext initialContext) throws NamingException {
|
||||
jndiEntry.register(initialContext);
|
||||
this.context = new AnnotationConfigApplicationContext(JtaAutoConfiguration.class);
|
||||
JtaTransactionManager transactionManager = this.context.getBean(JtaTransactionManager.class);
|
||||
if (jndiEntry.value instanceof UserTransaction) {
|
||||
assertThat(transactionManager.getUserTransaction()).isEqualTo(jndiEntry.value);
|
||||
assertThat(transactionManager.getTransactionManager()).isNull();
|
||||
}
|
||||
else {
|
||||
assertThat(transactionManager.getUserTransaction()).isInstanceOf(UserTransactionAdapter.class);
|
||||
assertThat(transactionManager.getTransactionManager()).isEqualTo(jndiEntry.value);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Arguments> transactionManagerJndiEntries() {
|
||||
return Arrays.asList(Arguments.of(new JndiEntry("java:comp/UserTransaction", UserTransaction.class)),
|
||||
Arguments.of(new JndiEntry("java:appserver/TransactionManager", TransactionManager.class)),
|
||||
Arguments.of(new JndiEntry("java:pm/TransactionManager", TransactionManager.class)),
|
||||
Arguments.of(new JndiEntry("java:/TransactionManager", TransactionManager.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customTransactionManager() {
|
||||
this.context = new AnnotationConfigApplicationContext(CustomTransactionManagerConfig.class,
|
||||
JtaAutoConfiguration.class);
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> this.context.getBean(JtaTransactionManager.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ExtendWith(JndiExtension.class)
|
||||
void disableJtaSupport(InitialContext initialContext) throws NamingException {
|
||||
new JndiEntry("java:comp/UserTransaction", UserTransaction.class).register(initialContext);
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("spring.jta.enabled:false").applyTo(this.context);
|
||||
this.context.register(JtaAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeansOfType(JtaTransactionManager.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomTransactionManagerConfig {
|
||||
|
||||
@Bean
|
||||
org.springframework.transaction.TransactionManager testTransactionManager() {
|
||||
return mock(org.springframework.transaction.TransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class JndiEntry {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final Object value;
|
||||
|
||||
private JndiEntry(String name, Class<?> type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.value = mock(type);
|
||||
}
|
||||
|
||||
private void register(InitialContext initialContext) throws NamingException {
|
||||
String[] components = this.name.split("/");
|
||||
String subcontextName = components[0];
|
||||
String entryName = components[1];
|
||||
Context javaComp = initialContext.createSubcontext(subcontextName);
|
||||
JndiLoader loader = new JndiLoader(initialContext.getEnvironment());
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(entryName + "/type", this.type.getName());
|
||||
properties.put(entryName + "/valueToConvert", this.value);
|
||||
loader.load(properties, javaComp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class JndiExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
|
||||
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
Namespace namespace = Namespace.create(getClass(), context.getUniqueId());
|
||||
context.getStore(namespace)
|
||||
.getOrComputeIfAbsent(InitialContext.class, (k) -> createInitialContext(), InitialContext.class);
|
||||
}
|
||||
|
||||
private InitialContext createInitialContext() {
|
||||
try {
|
||||
return new InitialContext();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
Namespace namespace = Namespace.create(getClass(), context.getUniqueId());
|
||||
InitialContext initialContext = context.getStore(namespace)
|
||||
.remove(InitialContext.class, InitialContext.class);
|
||||
initialContext.removeFromEnvironment("org.osjava.sj.jndi.ignoreClose");
|
||||
initialContext.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
|
||||
throws ParameterResolutionException {
|
||||
return InitialContext.class.isAssignableFrom(parameterContext.getParameter().getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
|
||||
throws ParameterResolutionException {
|
||||
Namespace namespace = Namespace.create(getClass(), extensionContext.getUniqueId());
|
||||
return extensionContext.getStore(namespace).get(InitialContext.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
java.naming.factory.initial = org.osjava.sj.SimpleJndiContextFactory
|
||||
org.osjava.sj.delimiter = /
|
||||
org.osjava.sj.jndi.shared = true
|
||||
org.osjava.sj.root = src/test/resources/simple-jndi
|
||||
org.osjava.sj.jndi.ignoreClose = true
|
||||
Reference in New Issue
Block a user