diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java index c9e9152d..880d3e38 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java @@ -53,7 +53,7 @@ import org.springframework.util.CollectionUtils; */ @Configuration @EnableTransactionManagement -@EnableConfigurationProperties(TaskProperties.class) +@EnableConfigurationProperties({ TaskProperties.class }) // @checkstyle:off @ConditionalOnProperty(prefix = "spring.cloud.task.autoconfiguration", name = "enabled", havingValue = "true", matchIfMissing = true) @@ -106,7 +106,8 @@ public class SimpleTaskAutoConfiguration { @Bean public TaskRepositoryInitializer taskRepositoryInitializer() { - TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer(); + TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer( + this.taskProperties); DataSource initializerDataSource = getDefaultConfigurer().getTaskDataSource(); if (initializerDataSource != null) { taskRepositoryInitializer.setDataSource(initializerDataSource); diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskProperties.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskProperties.java index 6d66b7f4..59d87615 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskProperties.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskProperties.java @@ -88,6 +88,14 @@ public class TaskProperties { */ private int singleInstanceLockCheckInterval = DEFAULT_CHECK_INTERVAL; + /** + * If set to true then tables are initialized. If set to false tables are not + * initialized. Defaults to null. The requirement for it to be defaulted to null is so + * that we can support the spring.cloud.task.initialize.enable until it + * is removed. + */ + private Boolean initializeEnabled; + public String getExternalExecutionId() { return this.externalExecutionId; } @@ -152,4 +160,12 @@ public class TaskProperties { this.singleInstanceLockCheckInterval = singleInstanceLockCheckInterval; } + public Boolean isInitializeEnabled() { + return initializeEnabled; + } + + public void setInitializeEnabled(Boolean initializeEnabled) { + this.initializeEnabled = initializeEnabled; + } + } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/TaskRepositoryInitializer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/TaskRepositoryInitializer.java index 09cd5817..79fcb7a9 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/TaskRepositoryInitializer.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/TaskRepositoryInitializer.java @@ -24,12 +24,12 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.task.configuration.TaskProperties; import org.springframework.core.io.ResourceLoader; import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.jdbc.support.MetaDataAccessException; -import org.springframework.util.StringUtils; /** * Utility for initializing the Task Repository's datasource. If a single @@ -38,8 +38,9 @@ import org.springframework.util.StringUtils; * is available in the current context, custom configuration of this is required (if * desired). * - * By default, initialization of the database can be disabled by configuring the property - * spring.cloud.task.initialize.enable to false. + * Initialization of the database can be disabled by configuring the property + * spring.cloud.task.initialize-enabled to false. + * spring.cloud.task.initialize.enable has been deprecated. * * @author Glenn Renfro * @author Michael Minella @@ -62,12 +63,12 @@ public final class TaskRepositoryInitializer implements InitializingBean { private ResourceLoader resourceLoader; @Value("${spring.cloud.task.initialize.enable:true}") - private boolean taskInitializationEnable; + private boolean taskInitializationEnabled; - @Value("${spring.cloud.task.tablePrefix:#{null}}") - private String tablePrefix; + private TaskProperties taskProperties; - public TaskRepositoryInitializer() { + public TaskRepositoryInitializer(TaskProperties taskProperties) { + this.taskProperties = taskProperties; } public void setDataSource(DataSource dataSource) { @@ -92,8 +93,11 @@ public final class TaskRepositoryInitializer implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { - if (this.dataSource != null && this.taskInitializationEnable - && !StringUtils.hasText(this.tablePrefix)) { + boolean isInitializeEnabled = (this.taskProperties.isInitializeEnabled() != null) + ? this.taskProperties.isInitializeEnabled() + : this.taskInitializationEnabled; + if (this.dataSource != null && isInitializeEnabled && this.taskProperties + .getTablePrefix().equals(TaskProperties.DEFAULT_TABLE_PREFIX)) { String platform = getDatabaseType(this.dataSource); if ("hsql".equals(platform)) { platform = "hsqldb"; diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TaskPropertiesTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TaskPropertiesTests.java index 10386647..0a8393a1 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TaskPropertiesTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TaskPropertiesTests.java @@ -43,13 +43,15 @@ public class TaskPropertiesTests { @Test public void test() { assertThat(this.taskProperties.getClosecontextEnabled()).isFalse(); + assertThat(this.taskProperties.isInitializeEnabled()).isFalse(); } @RunWith(SpringRunner.class) @SpringBootTest( classes = { TaskPropertiesTests.Config.class, SimpleTaskAutoConfiguration.class, SingleTaskConfiguration.class }, - properties = { "spring.cloud.task.closecontextEnabled=false" }) + properties = { "spring.cloud.task.closecontextEnabled=false", + "spring.cloud.task.initialize-enabled=false" }) @DirtiesContext public static class CloseContextEnabledTest extends TaskPropertiesTests { diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java index 71d7e42c..db4bbb02 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java @@ -50,7 +50,8 @@ public class TestConfiguration implements InitializingBean { @Bean public TaskRepositoryInitializer taskRepositoryInitializer() throws Exception { - TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer(); + TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer( + new TaskProperties()); taskRepositoryInitializer.setDataSource(this.dataSource); taskRepositoryInitializer.setResourceLoader(this.resourceLoader); taskRepositoryInitializer.afterPropertiesSet(); @@ -59,7 +60,7 @@ public class TestConfiguration implements InitializingBean { } @Bean - public TaskExplorer taskExplorer() throws Exception { + public TaskExplorer taskExplorer() { return new SimpleTaskExplorer(this.taskExecutionDaoFactoryBean); } @@ -79,7 +80,7 @@ public class TestConfiguration implements InitializingBean { } @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { if (this.dataSource != null) { this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean( this.dataSource); diff --git a/spring-cloud-task-docs/src/main/asciidoc/features.adoc b/spring-cloud-task-docs/src/main/asciidoc/features.adoc index 3c5b5307..284334d3 100644 --- a/spring-cloud-task-docs/src/main/asciidoc/features.adoc +++ b/spring-cloud-task-docs/src/main/asciidoc/features.adoc @@ -152,13 +152,16 @@ by setting the `spring.cloud.task.tablePrefix` to the prefix you need, as follow [[features-table-initialization]] === Enable/Disable table initialization In cases where you are creating the task tables and do not wish for Spring Cloud Task to -create them at task startup, set the `spring.cloud.task.initialize.enable` property to +create them at task startup, set the `spring.cloud.task.initialize-enabled` property to `false`, as follows: -`spring.cloud.task.initialize.enable=false` +`spring.cloud.task.initialize-enabled=false` It defaults to `true`. + +NOTE: The property `spring.cloud.task.initialize.enable` has been deprecated. + [[features-generated_task_id]] === Externally Generated Task ID diff --git a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/executionid/TaskStartTests.java b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/executionid/TaskStartTests.java index f509fa23..d930d1cf 100644 --- a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/executionid/TaskStartTests.java +++ b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/executionid/TaskStartTests.java @@ -127,7 +127,7 @@ public class TaskStartTests { this.properties.put("spring.datasource.driverClassName", DATASOURCE_DRIVER_CLASS_NAME); this.properties.put("spring.application.name", TASK_NAME); - this.properties.put("spring.cloud.task.initialize.enable", "false"); + this.properties.put("spring.cloud.task.initialize-enabled", "false"); JdbcTemplate template = new JdbcTemplate(this.dataSource); template.execute("DROP TABLE IF EXISTS TASK_TASK_BATCH"); diff --git a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/initializer/TaskInitializerTests.java b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/initializer/TaskInitializerTests.java new file mode 100644 index 00000000..4005e760 --- /dev/null +++ b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/initializer/TaskInitializerTests.java @@ -0,0 +1,225 @@ +/* + * Copyright 2016-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. + */ + +package org.springframework.cloud.task.initializer; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.h2.tools.Server; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.task.executionid.TaskStartApplication; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.TaskExplorer; +import org.springframework.cloud.task.repository.support.SimpleTaskExplorer; +import org.springframework.cloud.task.repository.support.TaskExecutionDaoFactoryBean; +import org.springframework.context.ApplicationContextException; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.SocketUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = { TaskInitializerTests.TaskLauncherConfiguration.class }) +public class TaskInitializerTests { + + private final static int WAIT_INTERVAL = 500; + + private final static int MAX_WAIT_TIME = 5000; + + private final static String URL = "maven://io.spring.cloud:" + + "timestamp-task:jar:1.1.0.RELEASE"; + + private final static String DATASOURCE_URL; + + private final static String DATASOURCE_USER_NAME = "SA"; + + private final static String DATASOURCE_USER_PASSWORD = "''"; + + private final static String DATASOURCE_DRIVER_CLASS_NAME = "org.h2.Driver"; + + private final static String TASK_NAME = "TASK_LAUNCHER_SINK_TEST"; + + private static int randomPort; + + static { + randomPort = SocketUtils.findAvailableTcpPort(); + DATASOURCE_URL = "jdbc:h2:tcp://localhost:" + randomPort + + "/mem:dataflow;DB_CLOSE_DELAY=-1;" + "DB_CLOSE_ON_EXIT=FALSE"; + } + + private DataSource dataSource; + + private TaskExplorer taskExplorer; + + private ConfigurableApplicationContext applicationContext; + + @After + public void tearDown() { + if (this.applicationContext != null && this.applicationContext.isActive()) { + this.applicationContext.close(); + } + } + + @Autowired + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean( + dataSource); + this.taskExplorer = new SimpleTaskExplorer(factoryBean); + } + + @Before + public void setup() { + + JdbcTemplate template = new JdbcTemplate(this.dataSource); + template.execute("DROP TABLE IF EXISTS TASK_TASK_BATCH"); + template.execute("DROP TABLE IF EXISTS TASK_SEQ"); + template.execute("DROP TABLE IF EXISTS TASK_EXECUTION_PARAMS"); + template.execute("DROP TABLE IF EXISTS TASK_EXECUTION"); + template.execute("DROP TABLE IF EXISTS TASK_LOCK"); + template.execute("DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ"); + template.execute("DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT"); + template.execute("DROP TABLE IF EXISTS BATCH_STEP_EXECUTION"); + template.execute("DROP TABLE IF EXISTS BATCH_JOB_SEQ"); + template.execute("DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ"); + template.execute("DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS"); + template.execute("DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT"); + template.execute("DROP TABLE IF EXISTS BATCH_JOB_EXECUTION"); + template.execute("DROP TABLE IF EXISTS BATCH_JOB_INSTANCE"); + template.execute("DROP SEQUENCE IF EXISTS TASK_SEQ"); + } + + @Test(expected = ApplicationContextException.class) + public void testNotInitialized() throws Exception { + SpringApplication myapp = new SpringApplication(TaskStartApplication.class); + String[] properties = { "--spring.cloud.task.initialize-enabled=false" }; + this.applicationContext = myapp.run(properties); + } + + @Test + public void testWithInitialized() throws Exception { + SpringApplication myapp = new SpringApplication(TaskStartApplication.class); + String[] properties = { "--spring.cloud.task.initialize-enabled=true" }; + this.applicationContext = myapp.run(properties); + assertThat(waitForDBToBePopulated()).isTrue(); + + Page taskExecutions = this.taskExplorer + .findAll(PageRequest.of(0, 10)); + TaskExecution te = taskExecutions.iterator().next(); + assertThat(taskExecutions.getTotalElements()).as("Only one row is expected") + .isEqualTo(1); + assertThat(taskExecutions.iterator().next().getExitCode().intValue()) + .as("return code should be 0").isEqualTo(0); + } + + @Test(expected = ApplicationContextException.class) + public void testNotInitializedOriginalProperty() throws Exception { + SpringApplication myapp = new SpringApplication(TaskStartApplication.class); + String[] properties = { "--spring.cloud.task.initialize.enable=false" }; + this.applicationContext = myapp.run(properties); + } + + @Test + public void testWithInitializedOriginalProperty() throws Exception { + SpringApplication myapp = new SpringApplication(TaskStartApplication.class); + String[] properties = { "--spring.cloud.task.initialize.enable=true" }; + this.applicationContext = myapp.run(properties); + assertThat(waitForDBToBePopulated()).isTrue(); + + Page taskExecutions = this.taskExplorer + .findAll(PageRequest.of(0, 10)); + TaskExecution te = taskExecutions.iterator().next(); + assertThat(taskExecutions.getTotalElements()).as("Only one row is expected") + .isEqualTo(1); + assertThat(taskExecutions.iterator().next().getExitCode().intValue()) + .as("return code should be 0").isEqualTo(0); + } + + private boolean tableExists() throws SQLException { + boolean result; + try (Connection conn = this.dataSource.getConnection(); + ResultSet res = conn.getMetaData().getTables(null, null, "TASK_EXECUTION", + new String[] { "TABLE" })) { + result = res.next(); + } + return result; + } + + private boolean waitForDBToBePopulated() throws Exception { + boolean isDbPopulated = false; + for (int waitTime = 0; waitTime <= MAX_WAIT_TIME; waitTime += WAIT_INTERVAL) { + Thread.sleep(WAIT_INTERVAL); + if (tableExists() && this.taskExplorer.getTaskExecutionCount() > 0) { + isDbPopulated = true; + break; + } + } + return isDbPopulated; + } + + @Configuration + public static class TaskLauncherConfiguration { + + private static Server defaultServer; + + @Bean + public Server initH2TCPServer() { + Server server = null; + try { + if (defaultServer == null) { + server = Server.createTcpServer("-ifNotExists", "-tcp", + "-tcpAllowOthers", "-tcpPort", String.valueOf(randomPort)) + .start(); + defaultServer = server; + } + } + catch (SQLException e) { + throw new IllegalStateException(e); + } + return defaultServer; + } + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(DATASOURCE_DRIVER_CLASS_NAME); + dataSource.setUrl(DATASOURCE_URL); + dataSource.setUsername(DATASOURCE_USER_NAME); + dataSource.setPassword(DATASOURCE_USER_PASSWORD); + return dataSource; + } + + } + +} diff --git a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java index 30b3f94a..ffc4d143 100644 --- a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java +++ b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java @@ -118,7 +118,6 @@ public class TaskLauncherSinkTests { this.properties.put("spring.datasource.driverClassName", DATASOURCE_DRIVER_CLASS_NAME); this.properties.put("spring.application.name", TASK_NAME); - this.properties.put("spring.cloud.task.initialize.enable", "false"); JdbcTemplate template = new JdbcTemplate(this.dataSource); template.execute("DROP ALL OBJECTS"); diff --git a/spring-cloud-task-samples/partitioned-batch-job/src/main/java/io/spring/JobConfiguration.java b/spring-cloud-task-samples/partitioned-batch-job/src/main/java/io/spring/JobConfiguration.java index 5e3c80db..fe3fea3a 100644 --- a/spring-cloud-task-samples/partitioned-batch-job/src/main/java/io/spring/JobConfiguration.java +++ b/spring-cloud-task-samples/partitioned-batch-job/src/main/java/io/spring/JobConfiguration.java @@ -87,7 +87,7 @@ public class JobConfiguration { List commandLineArgs = new ArrayList<>(3); commandLineArgs.add("--spring.profiles.active=worker"); - commandLineArgs.add("--spring.cloud.task.initialize.enable=false"); + commandLineArgs.add("--spring.cloud.task.initialize-enabled=false"); commandLineArgs.add("--spring.batch.initializer.enabled=false"); partitionHandler .setCommandLineArgsProvider(new PassThroughCommandLineArgsProvider(commandLineArgs));