Refactored database initialization

This commit introduces the `TaskRepositoryInitializer` component.  This
component will initialize the `DataSource` when appropriate and silently do
nothing otherwise.  It can be customized to initialize a specific
`DataSource` as required.

Resolves spring-cloud/spring-cloud-task#84
This commit is contained in:
Michael Minella
2016-02-11 08:28:23 -06:00
parent ff881f6546
commit d8e68d2558
4 changed files with 93 additions and 65 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -25,26 +25,25 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.cloud.task.listener.TaskLifecycleListener;
import org.springframework.cloud.task.repository.TaskNameResolver;
import org.springframework.cloud.task.repository.TaskRepository;
import org.springframework.cloud.task.repository.support.SimpleTaskNameResolver;
import org.springframework.cloud.task.repository.support.TaskDatabaseInitializer;
import org.springframework.cloud.task.repository.support.TaskRepositoryInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Base {@code Configuration} class providing common structure for enabling and using
* Spring Task. Customization is
* available by implementing the {@link TaskConfigurer} interface.
* Spring Task. Customization is available by implementing the {@link TaskConfigurer}
* interface.
*
* @author Glenn Renfro
* @author Michael Minella
*/
@Configuration
@EnableTransactionManagement
@@ -58,15 +57,9 @@ public class SimpleTaskConfiguration {
@Autowired(required = false)
private Collection<DataSource> dataSources;
@Autowired
private ResourceLoader resourceLoader;
@Autowired(required = false)
private ApplicationArguments applicationArguments;
@Value("${spring.class.initialize.enable:true}")
private boolean taskInitializationEnable;
private boolean initialized = false;
private TaskRepository taskRepository;
@@ -95,6 +88,17 @@ public class SimpleTaskConfiguration {
return new SimpleTaskNameResolver();
}
@Bean
public TaskRepositoryInitializer taskRepositoryInitializer() {
TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer();
if(this.dataSources != null && this.dataSources.size() == 1) {
taskRepositoryInitializer.setDataSource(this.dataSources.iterator().next());
}
return taskRepositoryInitializer;
}
/**
* Sets up the basic components by extracting them from the {@link TaskConfigurer}, defaulting to some
* sensible values as long as a unique DataSource is available.
@@ -116,7 +120,7 @@ public class SimpleTaskConfiguration {
}
private TaskConfigurer getDefaultConfigurer(Collection<TaskConfigurer> configurers) {
boolean isDataSourceConfigured = (dataSources != null && !dataSources.isEmpty());
boolean isDataSourceConfigured = (dataSources != null && dataSources.size() == 1);
verifyEnvironment(configurers);
if (configurers == null || configurers.isEmpty()) {
if (!isDataSourceConfigured) {
@@ -124,12 +128,7 @@ public class SimpleTaskConfiguration {
return this.configurer;
}
else {
DataSource dataSource = dataSources.iterator().next();
if(taskInitializationEnable) {
logger.debug("Initializing Task Schema");
TaskDatabaseInitializer.initializeDatabase(dataSource, resourceLoader);
}
this.configurer = new DefaultTaskConfigurer(dataSource);
this.configurer = new DefaultTaskConfigurer(this.dataSources.iterator().next());
return this.configurer;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -20,18 +20,32 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log;
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.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.support.MetaDataAccessException;
/**
* Utility for initializing the Task Repository's datasource. If a single
* {@link DataSource} is available in the current context, and functionality is enabled
* (as it is by default), this will initialize the database. If more than one DataSource
* 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
* <code>spring.cloud.task.initialize.enable</code> to false.
*
* @author Glenn Renfro
* @author Michael Minella
*/
public final class TaskDatabaseInitializer {
public final class TaskRepositoryInitializer implements InitializingBean {
private static final Log logger = LogFactory.getLog(TaskDatabaseInitializer.class);
private static final Log logger = LogFactory.getLog(TaskRepositoryInitializer.class);
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/"
+ "cloud/task/schema-@@platform@@.sql";
@@ -41,12 +55,37 @@ public final class TaskDatabaseInitializer {
*/
private static String schema = DEFAULT_SCHEMA_LOCATION;
private TaskDatabaseInitializer(){
private DataSource dataSource;
private ResourceLoader resourceLoader;
@Value("${spring.cloud.task.initialize.enable:true}")
private boolean taskInitializationEnable;
public TaskRepositoryInitializer(){
}
public static void initializeDatabase(DataSource dataSource, ResourceLoader resourceLoader) {
if (dataSource != null) {
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Autowired(required = false)
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
private String getDatabaseType(DataSource dataSource) {
try {
return DatabaseType.fromMetaData(dataSource).toString().toLowerCase();
}
catch (MetaDataAccessException ex) {
throw new IllegalStateException("Unable to detect database type", ex);
}
}
@Override
public void afterPropertiesSet() throws Exception {
if (dataSource != null && taskInitializationEnable) {
String platform = getDatabaseType(dataSource);
if ("hsql".equals(platform)) {
platform = "hsqldb";
@@ -70,14 +109,4 @@ public final class TaskDatabaseInitializer {
DatabasePopulatorUtils.execute(populator, dataSource);
}
}
private static String getDatabaseType(DataSource dataSource) {
try {
return DatabaseType.fromMetaData(dataSource).toString().toLowerCase();
}
catch (MetaDataAccessException ex) {
throw new IllegalStateException("Unable to detect database type", ex);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -26,7 +26,7 @@ import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao;
import org.springframework.cloud.task.repository.dao.TaskExecutionDao;
import org.springframework.cloud.task.repository.support.SimpleTaskExplorer;
import org.springframework.cloud.task.repository.support.SimpleTaskRepository;
import org.springframework.cloud.task.repository.support.TaskDatabaseInitializer;
import org.springframework.cloud.task.repository.support.TaskRepositoryInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
@@ -46,6 +46,16 @@ public class TestConfiguration {
@Autowired(required = false)
private ResourceLoader resourceLoader;
@Bean
public TaskRepositoryInitializer taskRepositoryInitializer() throws Exception {
TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer();
taskRepositoryInitializer.setDataSource(this.dataSource);
taskRepositoryInitializer.setResourceLoader(this.resourceLoader);
taskRepositoryInitializer.afterPropertiesSet();
return taskRepositoryInitializer;
}
@Bean
public TaskRepository taskRepository(TaskExecutionDao taskExecutionDao){
return new SimpleTaskRepository(taskExecutionDao);
@@ -57,8 +67,6 @@ public class TestConfiguration {
return new ResourcelessTransactionManager();
}
else {
TaskDatabaseInitializer.initializeDatabase(dataSource, this.resourceLoader);
return new DataSourceTransactionManager(dataSource);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -18,10 +18,10 @@ package org.springframework.cloud.task.repository.support;
import javax.sql.DataSource;
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.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.cloud.task.configuration.SimpleTaskConfiguration;
@@ -30,42 +30,30 @@ import org.springframework.cloud.task.repository.TaskRepository;
import org.springframework.cloud.task.util.TaskExecutionCreator;
import org.springframework.cloud.task.util.TestDBUtils;
import org.springframework.cloud.task.util.TestVerifierUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Tests for the SimpleTaskRepository that uses JDBC as a datastore.
*
* @author Glenn Renfro.
* @author Michael Minella
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {EmbeddedDataSourceConfiguration.class,
SimpleTaskConfiguration.class,
PropertyPlaceholderAutoConfiguration.class})
public class SimpleTaskRepositoryJdbcTests {
@Autowired
private TaskRepository taskRepository;
@Autowired
private DataSource dataSource;
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(SimpleTaskConfiguration.class,
EmbeddedDataSourceConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
dataSource = this.context.getBean(DataSource.class);
JdbcTaskRepositoryFactoryBean factoryBean =
new JdbcTaskRepositoryFactoryBean(dataSource);
taskRepository = factoryBean.getObject();
}
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
@DirtiesContext
public void testCreateTaskExecutionNoParam() {
TaskExecution expectedTaskExecution =
TaskExecutionCreator.createAndStoreTaskExecutionNoParams(taskRepository);
@@ -75,6 +63,7 @@ public class SimpleTaskRepositoryJdbcTests {
}
@Test
@DirtiesContext
public void testCreateTaskExecutionWithParam() {
TaskExecution expectedTaskExecution =
TaskExecutionCreator.createAndStoreTaskExecutionWithParams(taskRepository);
@@ -84,6 +73,7 @@ public class SimpleTaskRepositoryJdbcTests {
}
@Test
@DirtiesContext
public void testUpdateTaskExecution() {
TaskExecution expectedTaskExecution =
TaskExecutionCreator.createAndStoreTaskExecutionNoParams(taskRepository);
@@ -95,6 +85,7 @@ public class SimpleTaskRepositoryJdbcTests {
}
@Test
@DirtiesContext
public void testCreateTaskExecutionNoParamMaxExitMessageSize(){
TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoParam();
expectedTaskExecution.setExitMessage(new String(new char[SimpleTaskRepository.MAX_EXIT_MESSAGE_SIZE+1]));
@@ -102,6 +93,7 @@ public class SimpleTaskRepositoryJdbcTests {
}
@Test(expected=IllegalArgumentException.class)
@DirtiesContext
public void testCreateTaskExecutionNoParamMaxTaskName(){
TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoParam();
expectedTaskExecution.setTaskName(new String(new char[SimpleTaskRepository.MAX_TASK_NAME_SIZE+1]));