BATCH-2110: Updated batch to support Spring 4

* Refactored iBatis based readers and writers to not utilize
 SqlMapClientTemplate.
* Depricated all iBatis based readers and writers in favor of MyBatis's
 native Spring support.
* Updated XStream support to 1.4.4 and Jettison to 1.2 to be in
 alignment with Spring 4.
* Added the PooledEmbeddedDataSource to address the issue outlined in
 SPR-11372.
This commit is contained in:
Michael Minella
2014-01-29 22:21:00 -06:00
parent 3bdbfac0d9
commit b278239751
51 changed files with 1130 additions and 1685 deletions

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2014 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
*
* http://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.batch.core;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Logger;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
/**
* As of Spring 3.2, when a context is closed, the shutdown method is
* called on any beans that are registered. With an embedded database
* that uses a connection pool, this can leave the connection pool open
* with stale connections. This wraps an {@link EmbeddedDatabase} and
* ignores calls to {@link EmbeddedDatabase#shutdown()}.
*
* @author Phil Webb
* @since 3.0
*/
public class PooledEmbeddedDataSource implements EmbeddedDatabase {
private final EmbeddedDatabase dataSource;
/**
* @param dataSource The database to be wrapped
*/
public PooledEmbeddedDataSource(EmbeddedDatabase dataSource) {
this.dataSource = dataSource;
}
/* (non-Javadoc)
* @see javax.sql.DataSource#getConnection()
*/
@Override
public Connection getConnection() throws SQLException {
return this.dataSource.getConnection();
}
/* (non-Javadoc)
* @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
*/
@Override
public Connection getConnection(String username, String password) throws SQLException {
return this.dataSource.getConnection(username, password);
}
/* (non-Javadoc)
* @see javax.sql.CommonDataSource#getLogWriter()
*/
@Override
public PrintWriter getLogWriter() throws SQLException {
return this.dataSource.getLogWriter();
}
/* (non-Javadoc)
* @see javax.sql.CommonDataSource#setLogWriter(java.io.PrintWriter)
*/
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
this.dataSource.setLogWriter(out);
}
/* (non-Javadoc)
* @see javax.sql.CommonDataSource#getLoginTimeout()
*/
@Override
public int getLoginTimeout() throws SQLException {
return this.dataSource.getLoginTimeout();
}
/* (non-Javadoc)
* @see javax.sql.CommonDataSource#setLoginTimeout(int)
*/
@Override
public void setLoginTimeout(int seconds) throws SQLException {
this.dataSource.setLoginTimeout(seconds);
}
/* (non-Javadoc)
* @see java.sql.Wrapper#unwrap(java.lang.Class)
*/
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return this.dataSource.unwrap(iface);
}
/* (non-Javadoc)
* @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
*/
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return this.dataSource.isWrapperFor(iface);
}
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
/* (non-Javadoc)
* @see org.springframework.jdbc.datasource.embedded.EmbeddedDatabase#shutdown()
*/
@Override
public void shutdown() {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007 the original author or authors.
* Copyright 2006-2014 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.
@@ -16,8 +16,10 @@
package org.springframework.batch.core;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.batch.core.job.JobSupport;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.ChildBeanDefinition;
@@ -25,34 +27,42 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
public class SpringBeanJobTests extends TestCase {
public class SpringBeanJobTests {
@Test
public void testBeanName() throws Exception {
StaticApplicationContext context = new StaticApplicationContext();
JobSupport configuration = new JobSupport();
context.getAutowireCapableBeanFactory().initializeBean(configuration,
"bean");
context.refresh();
assertNotNull(configuration.getName());
configuration.setBeanName("foo");
context.getAutowireCapableBeanFactory().initializeBean(configuration,
"bean");
assertEquals("bean", configuration.getName());
context.close();
}
@Test
public void testBeanNameWithBeanDefinition() throws Exception {
GenericApplicationContext context = new GenericApplicationContext();
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue("foo");
context.registerBeanDefinition("bean", new RootBeanDefinition(
JobSupport.class, args, null));
context.refresh();
JobSupport configuration = (JobSupport) context
.getBean("bean");
assertNotNull(configuration.getName());
assertEquals("foo", configuration.getName());
configuration.setBeanName("bar");
assertEquals("foo", configuration.getName());
context.close();
}
@Test
public void testBeanNameWithParentBeanDefinition() throws Exception {
GenericApplicationContext context = new GenericApplicationContext();
ConstructorArgumentValues args = new ConstructorArgumentValues();
@@ -60,6 +70,7 @@ public class SpringBeanJobTests extends TestCase {
context.registerBeanDefinition("parent", new RootBeanDefinition(
JobSupport.class, args, null));
context.registerBeanDefinition("bean", new ChildBeanDefinition("parent"));
context.refresh();
JobSupport configuration = (JobSupport) context
.getBean("bean");
assertNotNull(configuration.getName());
@@ -68,5 +79,6 @@ public class SpringBeanJobTests extends TestCase {
assertEquals("bar", configuration.getName());
configuration.setName("foo");
assertEquals("foo", configuration.getName());
context.close();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2012-2014 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,6 +18,7 @@ package org.springframework.batch.core.configuration.annotation;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.springframework.batch.core.PooledEmbeddedDataSource;
import org.springframework.batch.core.Step;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -31,13 +32,13 @@ import org.springframework.util.ClassUtils;
@Configuration
public class DataSourceConfiguration {
@Autowired
private Environment environment;
@Autowired
private ResourceLoader resourceLoader;
@PostConstruct
protected void initialize() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
@@ -45,10 +46,10 @@ public class DataSourceConfiguration {
populator.setContinueOnError(true);
DatabasePopulatorUtils.execute(populator, dataSource());
}
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseFactory().getDatabase();
return new PooledEmbeddedDataSource(new EmbeddedDatabaseFactory().getDatabase());
}
}

View File

@@ -42,7 +42,7 @@ import org.springframework.context.annotation.Configuration;
/**
* @author Dave Syer
*
*
*/
public class JobBuilderConfigurationTests {

View File

@@ -15,6 +15,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.PooledEmbeddedDataSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
public class JsrJobParametersConverterTests {
@@ -24,10 +25,10 @@ public class JsrJobParametersConverterTests {
@BeforeClass
public static void setupDatabase() {
dataSource = new EmbeddedDatabaseBuilder().
dataSource = new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder().
addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql").
addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql").
build();
build());
}
@Before

View File

@@ -23,6 +23,7 @@ import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.batch.core.PooledEmbeddedDataSource;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.xml.DummyItemProcessor;
@@ -101,10 +102,10 @@ public class BatchParserTests {
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder().
return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder().
addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql").
addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql").
build();
build());
}
}
}

View File

@@ -10,22 +10,22 @@ public class JdbcExecutionContextDaoTests extends AbstractExecutionContextDaoTes
@Override
protected JobInstanceDao getJobInstanceDao() {
return (JobInstanceDao) applicationContext.getBean("jobInstanceDao", JobInstanceDao.class);
return applicationContext.getBean("jobInstanceDao", JobInstanceDao.class);
}
@Override
protected JobExecutionDao getJobExecutionDao() {
return (JobExecutionDao) applicationContext.getBean("jobExecutionDao", JdbcJobExecutionDao.class);
return applicationContext.getBean("jobExecutionDao", JdbcJobExecutionDao.class);
}
@Override
protected StepExecutionDao getStepExecutionDao() {
return (StepExecutionDao) applicationContext.getBean("stepExecutionDao", StepExecutionDao.class);
return applicationContext.getBean("stepExecutionDao", StepExecutionDao.class);
}
@Override
protected ExecutionContextDao getExecutionContextDao() {
return (ExecutionContextDao) applicationContext.getBean("executionContextDao", JdbcExecutionContextDao.class);
return applicationContext.getBean("executionContextDao", JdbcExecutionContextDao.class);
}
}

View File

@@ -1,16 +1,17 @@
package org.springframework.batch.core.repository.dao;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.ExitStatus;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)

View File

@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
@@ -30,6 +31,15 @@ public class XStreamExecutionContextStringSerializerTests {
serializer = serializerDeserializer;
}
@Test
public void testSerializePropertiesArrayContainer() throws Exception {
PropertiesArrayContainer container = new PropertiesArrayContainer();
container.setProps(new Properties[3]);
PropertiesArrayContainer result = (PropertiesArrayContainer) serializationRoundTrip(container);
assertEquals(3, result.getProps().length);
}
@Test
public void testSerializeAMap() throws Exception {
Map<String, Object> m1 = new HashMap<String, Object>();
@@ -39,7 +49,7 @@ public class XStreamExecutionContextStringSerializerTests {
m1.put("object3", new Date(123456790123L));
m1.put("object4", new Double(1234567.1234D));
Map<String, Object> m2 = serializationRoundTrip(m1);
Map<String, Object> m2 = (Map<String, Object>) serializationRoundTrip(m1);
compareContexts(m1, m2);
}
@@ -61,7 +71,7 @@ public class XStreamExecutionContextStringSerializerTests {
o1.setObj(o2);
m1.put("co", o1);
Map<String, Object> m2 = serializationRoundTrip(m1);
Map<String, Object> m2 = (Map<String, Object>) serializationRoundTrip(m1);
compareContexts(m1, m2);
}
@@ -80,14 +90,14 @@ public class XStreamExecutionContextStringSerializerTests {
}
@SuppressWarnings("unchecked")
private Map<String, Object> serializationRoundTrip(Map<String, Object> m1) throws IOException {
private Object serializationRoundTrip(Object m1) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializer.serialize(m1, out);
String s = out.toString();
ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
Map<String, Object> m2 = (Map<String, Object>) serializer.deserialize(in);
Object m2 = serializer.deserialize(in);
return m2;
}
@@ -133,15 +143,27 @@ public class XStreamExecutionContextStringSerializerTests {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComplexObject that = (ComplexObject) o;
if (map != null ? !map.equals(that.map) : that.map != null) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
if (number != null ? !number.equals(that.number) : that.number != null) return false;
if (obj != null ? !obj.equals(that.obj) : that.obj != null) return false;
if (map != null ? !map.equals(that.map) : that.map != null) {
return false;
}
if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
}
if (number != null ? !number.equals(that.number) : that.number != null) {
return false;
}
if (obj != null ? !obj.equals(that.obj) : that.obj != null) {
return false;
}
return true;
}
@@ -160,6 +182,17 @@ public class XStreamExecutionContextStringSerializerTests {
public String toString() {
return "ComplexObject [name=" + name + ", number=" + number + "]";
}
}
public static class PropertiesArrayContainer {
private Properties[] props;
public Properties[] getProps() {
return props;
}
public void setProps(Properties[] props) {
this.props = props;
}
}
}

View File

@@ -25,7 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
/**
* Repository tests using JDBC DAOs (rather than mocks).
*
*
* @author Robert Kasanicky
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -186,4 +186,4 @@ public class SimpleJobRepositoryIntegrationTests {
assertEquals(jobExecution, jobRepository.getLastJobExecution(job.getName(), jobParameters));
}
}
}

View File

@@ -15,6 +15,7 @@ import org.springframework.batch.core.ItemWriteListener;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.PooledEmbeddedDataSource;
import org.springframework.batch.core.SkipListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
@@ -184,11 +185,11 @@ public class RegisterMultiListenerTests {
@Bean
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder()
.addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
.addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
.setType(EmbeddedDatabaseType.HSQL)
.build();
.build());
}
@Override
@@ -214,11 +215,11 @@ public class RegisterMultiListenerTests {
@Bean
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder()
.addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
.addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
.setType(EmbeddedDatabaseType.HSQL)
.build();
.build());
}
@Override

View File

@@ -53,7 +53,7 @@ import org.springframework.transaction.support.TransactionTemplate;
/**
* @author Dave Syer
*
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/org/springframework/batch/core/repository/dao/sql-dao-test.xml")
@@ -138,12 +138,12 @@ public class AsyncChunkOrientedStepIntegrationTests {
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
// Need a transaction so one connection is enough to get job execution and its parameters
StepExecution lastStepExecution = new TransactionTemplate(transactionManager)
.execute(new TransactionCallback<StepExecution>() {
@Override
public StepExecution doInTransaction(TransactionStatus status) {
return jobRepository.getLastStepExecution(jobExecution.getJobInstance(), step.getName());
}
});
.execute(new TransactionCallback<StepExecution>() {
@Override
public StepExecution doInTransaction(TransactionStatus status) {
return jobRepository.getLastStepExecution(jobExecution.getJobInstance(), step.getName());
}
});
assertEquals(lastStepExecution, stepExecution);
assertFalse(lastStepExecution == stepExecution);
}