Skip to content

Commit 00b8aca

Browse files
mminellacppwfs
authored andcommitted
Added support for specifying a TransactionManager
Before this commit, Spring Cloud Task did not correctly configure the provided PlatformTransactionManager with the TaskRepository. This commit addressis this bug. Resolves #652 Polished PR on Merge
1 parent f7c2501 commit 00b8aca

File tree

3 files changed

+240
-13
lines changed

3 files changed

+240
-13
lines changed

spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2019 the original author or authors.
2+
* Copyright 2015-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@
2828
import org.springframework.aop.scope.ScopedProxyUtils;
2929
import org.springframework.beans.factory.annotation.Autowired;
3030
import org.springframework.boot.ApplicationArguments;
31-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3231
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3332
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3433
import org.springframework.cloud.task.repository.TaskExplorer;
@@ -89,8 +88,7 @@ public TaskRepository taskRepository() {
8988
}
9089

9190
@Bean
92-
@ConditionalOnMissingBean
93-
public PlatformTransactionManager transactionManager() {
91+
public PlatformTransactionManager springCloudTaskTransactionManager() {
9492
return this.platformTransactionManager;
9593
}
9694

spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskRepository.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2019 the original author or authors.
2+
* Copyright 2015-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
* information.
2727
*
2828
* @author Glenn Renfro
29+
* @author Michael Minella
2930
*/
3031
public interface TaskRepository {
3132

@@ -37,7 +38,7 @@ public interface TaskRepository {
3738
* @param exitMessage to be stored for the task.
3839
* @return the updated {@link TaskExecution}
3940
*/
40-
@Transactional
41+
@Transactional("springCloudTaskTransactionManager")
4142
TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date endTime,
4243
String exitMessage);
4344

@@ -51,7 +52,7 @@ TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date end
5152
* @return the updated {@link TaskExecution}
5253
* @since 1.1.0
5354
*/
54-
@Transactional
55+
@Transactional("springCloudTaskTransactionManager")
5556
TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date endTime,
5657
String exitMessage, String errorMessage);
5758

@@ -64,7 +65,7 @@ TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date end
6465
* TaskExecution's taskExecutionId will also contain the id that was used to store the
6566
* TaskExecution.
6667
*/
67-
@Transactional
68+
@Transactional("springCloudTaskTransactionManager")
6869
TaskExecution createTaskExecution(TaskExecution taskExecution);
6970

7071
/**
@@ -75,7 +76,7 @@ TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date end
7576
* @param name task name to be associated with the task execution.
7677
* @return the initial {@link TaskExecution}
7778
*/
78-
@Transactional
79+
@Transactional("springCloudTaskTransactionManager")
7980
TaskExecution createTaskExecution(String name);
8081

8182
/**
@@ -85,7 +86,7 @@ TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date end
8586
* launching, etc).
8687
* @return the initial {@link TaskExecution}
8788
*/
88-
@Transactional
89+
@Transactional("springCloudTaskTransactionManager")
8990
TaskExecution createTaskExecution();
9091

9192
/**
@@ -97,7 +98,7 @@ TaskExecution completeTaskExecution(long executionId, Integer exitCode, Date end
9798
* @param externalExecutionId id assigned to the task by the platform.
9899
* @return TaskExecution created based on the parameters.
99100
*/
100-
@Transactional
101+
@Transactional("springCloudTaskTransactionManager")
101102
TaskExecution startTaskExecution(long executionid, String taskName, Date startTime,
102103
List<String> arguments, String externalExecutionId);
103104

@@ -106,7 +107,7 @@ TaskExecution startTaskExecution(long executionid, String taskName, Date startTi
106107
* @param executionid to the task execution to be updated.
107108
* @param externalExecutionId id assigned to the task by the platform.
108109
*/
109-
@Transactional
110+
@Transactional("springCloudTaskTransactionManager")
110111
void updateExternalExecutionId(long executionid, String externalExecutionId);
111112

112113
/**
@@ -120,7 +121,7 @@ TaskExecution startTaskExecution(long executionid, String taskName, Date startTi
120121
* @return A TaskExecution that contains the information available at the beginning of
121122
* a TaskExecution.
122123
*/
123-
@Transactional
124+
@Transactional("springCloudTaskTransactionManager")
124125
TaskExecution startTaskExecution(long executionid, String taskName, Date startTime,
125126
List<String> arguments, String externalExecutionId, Long parentExecutionId);
126127

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* Copyright 2020-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.task.configuration;
18+
19+
import java.util.ArrayList;
20+
import java.util.Date;
21+
22+
import javax.sql.DataSource;
23+
24+
import org.junit.Test;
25+
26+
import org.springframework.boot.autoconfigure.AutoConfigurations;
27+
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
28+
import org.springframework.boot.jdbc.DataSourceBuilder;
29+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
30+
import org.springframework.cloud.task.listener.TaskLifecycleListener;
31+
import org.springframework.cloud.task.repository.TaskExecution;
32+
import org.springframework.cloud.task.repository.TaskRepository;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.jdbc.core.JdbcTemplate;
36+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
37+
import org.springframework.test.jdbc.JdbcTestUtils;
38+
import org.springframework.test.util.ReflectionTestUtils;
39+
import org.springframework.transaction.PlatformTransactionManager;
40+
import org.springframework.transaction.support.DefaultTransactionStatus;
41+
42+
import static org.assertj.core.api.Assertions.assertThat;
43+
44+
/**
45+
* @author Michael Minella
46+
*/
47+
public class RepositoryTransactionManagerConfigurationTests {
48+
49+
@Test
50+
public void testZeroCustomTransactionManagerConfiguration() {
51+
ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner()
52+
.withConfiguration(
53+
AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class,
54+
SimpleTaskAutoConfiguration.class,
55+
ZeroTransactionManagerConfiguration.class))
56+
.withPropertyValues("application.name=transactionManagerTask");
57+
58+
applicationContextRunner.run((context) -> {
59+
DataSource dataSource = context.getBean("dataSource", DataSource.class);
60+
61+
int taskExecutionCount = JdbcTestUtils
62+
.countRowsInTable(new JdbcTemplate(dataSource), "TASK_EXECUTION");
63+
64+
assertThat(taskExecutionCount).isEqualTo(1);
65+
});
66+
}
67+
68+
@Test
69+
public void testSingleCustomTransactionManagerConfiguration() {
70+
testConfiguration(SingleTransactionManagerConfiguration.class);
71+
}
72+
73+
@Test
74+
public void testMultipleCustomTransactionManagerConfiguration() {
75+
testConfiguration(MultipleTransactionManagerConfiguration.class);
76+
}
77+
78+
private void testConfiguration(Class configurationClass) {
79+
ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner()
80+
.withConfiguration(
81+
AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class,
82+
SimpleTaskAutoConfiguration.class, configurationClass))
83+
.withPropertyValues("application.name=transactionManagerTask");
84+
85+
applicationContextRunner.run((context) -> {
86+
DataSource dataSource = context.getBean("dataSource", DataSource.class);
87+
88+
int taskExecutionCount = JdbcTestUtils
89+
.countRowsInTable(new JdbcTemplate(dataSource), "TASK_EXECUTION");
90+
91+
// Verify that the create call was rolled back
92+
assertThat(taskExecutionCount).isEqualTo(0);
93+
94+
// Execute a new create call so that things close cleanly
95+
TaskRepository taskRepository = context.getBean("taskRepository",
96+
TaskRepository.class);
97+
98+
TaskExecution taskExecution = taskRepository
99+
.createTaskExecution("transactionManagerTask");
100+
taskExecution = taskRepository.startTaskExecution(
101+
taskExecution.getExecutionId(), taskExecution.getTaskName(),
102+
new Date(), new ArrayList<>(0), null);
103+
104+
TaskLifecycleListener listener = context.getBean(TaskLifecycleListener.class);
105+
106+
ReflectionTestUtils.setField(listener, "taskExecution", taskExecution);
107+
});
108+
}
109+
110+
@EnableTask
111+
@Configuration
112+
public static class ZeroTransactionManagerConfiguration {
113+
114+
@Bean
115+
public TaskConfigurer taskConfigurer(DataSource dataSource) {
116+
return new DefaultTaskConfigurer(dataSource);
117+
}
118+
119+
@Bean
120+
public DataSource dataSource() {
121+
DataSourceBuilder dsb = DataSourceBuilder.create()
122+
.url("jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE");
123+
dsb.driverClassName("org.h2.Driver");
124+
return dsb.build();
125+
}
126+
127+
}
128+
129+
@EnableTask
130+
@Configuration
131+
public static class SingleTransactionManagerConfiguration {
132+
133+
@Bean
134+
public TaskConfigurer taskConfigurer(DataSource dataSource,
135+
PlatformTransactionManager transactionManager) {
136+
return new DefaultTaskConfigurer(dataSource) {
137+
@Override
138+
public PlatformTransactionManager getTransactionManager() {
139+
return transactionManager;
140+
}
141+
};
142+
}
143+
144+
@Bean
145+
public DataSource dataSource() {
146+
DataSourceBuilder dsb = DataSourceBuilder.create()
147+
.url("jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE");
148+
dsb.driverClassName("org.h2.Driver");
149+
return dsb.build();
150+
}
151+
152+
@Bean
153+
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
154+
return new TestDataSourceTransactionManager(dataSource);
155+
}
156+
157+
}
158+
159+
@EnableTask
160+
@Configuration
161+
public static class MultipleTransactionManagerConfiguration {
162+
163+
@Bean
164+
public TaskConfigurer taskConfigurer(DataSource dataSource,
165+
PlatformTransactionManager transactionManager) {
166+
return new DefaultTaskConfigurer(dataSource) {
167+
@Override
168+
public PlatformTransactionManager getTransactionManager() {
169+
return transactionManager;
170+
}
171+
};
172+
}
173+
174+
@Bean
175+
public DataSource dataSource() {
176+
DataSourceBuilder dsb = DataSourceBuilder.create()
177+
.url("jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE");
178+
dsb.driverClassName("org.h2.Driver");
179+
return dsb.build();
180+
}
181+
182+
@Bean
183+
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
184+
return new TestDataSourceTransactionManager(dataSource);
185+
}
186+
187+
@Bean
188+
public DataSource dataSource2() {
189+
DataSourceBuilder dsb = DataSourceBuilder.create()
190+
.url("jdbc:h2:mem:testdb2;DB_CLOSE_ON_EXIT=FALSE");
191+
dsb.driverClassName("org.h2.Driver");
192+
return dsb.build();
193+
}
194+
195+
@Bean
196+
public DataSourceTransactionManager transactionManager2(DataSource dataSource2) {
197+
return new DataSourceTransactionManager(dataSource2);
198+
}
199+
200+
}
201+
202+
private static class TestDataSourceTransactionManager
203+
extends DataSourceTransactionManager {
204+
205+
protected TestDataSourceTransactionManager(DataSource dataSource) {
206+
super(dataSource);
207+
}
208+
209+
private int count = 0;
210+
211+
@Override
212+
protected void doCommit(DefaultTransactionStatus status) {
213+
214+
if (count == 0) {
215+
// Rollback the finish of the task
216+
super.doRollback(status);
217+
}
218+
else {
219+
// Commit the start of the task
220+
super.doCommit(status);
221+
}
222+
223+
count++;
224+
}
225+
226+
}
227+
228+
}

0 commit comments

Comments
 (0)