Refactored bootstrapping of JSR-352 base context

Spring Batch's implementation of JSR-352 previously relied on Spring's
ContextSingletonBeanFactoryLocator.  However, this has been removed in
Spring 5 as a relic of older EJB based use cases.  This commit now
lazily bootstraps the base context on it's own when the first
JsrJobOperator is requested.

Resolves BATCH-2572
This commit is contained in:
Michael Minella
2017-03-08 17:06:29 -06:00
parent 342d27bc1e
commit a6c4dacbbe
9 changed files with 101 additions and 78 deletions

View File

@@ -48,7 +48,7 @@ allprojects {
environmentProperty = project.hasProperty('environment') ? getProperty('environment') : 'hsql'
springVersionDefault = '4.3.6.RELEASE'
springVersionDefault = '5.0.0.M5'
springVersion = project.hasProperty('springVersion') ? getProperty('springVersion') : springVersionDefault
springRetryVersion = '1.2.0.RELEASE'
springAmqpVersion = '1.5.6.RELEASE'
@@ -842,5 +842,5 @@ or specify the Gradle property `TCK_HOME`, e.g: ./gradlew runTck -PTCK_HOME=/pat
task wrapper(type: Wrapper) {
description = 'Generates gradlew[.bat] scripts'
gradleVersion = '3.0'
gradleVersion = '3.4'
}

View File

@@ -1,6 +1,6 @@
#Thu Sep 29 16:06:24 CDT 2016
#Wed Mar 08 11:23:55 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2017 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,6 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import javax.batch.operations.BatchRuntimeException;
import javax.batch.operations.JobExecutionAlreadyCompleteException;
import javax.batch.operations.JobExecutionIsRunningException;
@@ -46,6 +45,7 @@ import javax.batch.runtime.StepExecution;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
@@ -69,15 +69,13 @@ import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
@@ -160,9 +158,8 @@ public class JsrJobOperator implements JobOperator, ApplicationContextAware, Ini
* one if it has) to populate itself.
*/
public JsrJobOperator() {
BeanFactoryLocator beanFactoryLocactor = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference ref = beanFactoryLocactor.useBeanFactory("baseContext");
baseContext = (ApplicationContext) ref.getFactory();
this.baseContext = BaseContextHolder.getInstance().getContext();
baseContext.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
@@ -805,4 +802,47 @@ public class JsrJobOperator implements JobOperator, ApplicationContextAware, Ini
}
}
}
/**
* A singleton holder used to lazily bootstrap the base context used in JSR-352.
*/
protected static class BaseContextHolder {
private ApplicationContext context;
private static BaseContextHolder instance;
private BaseContextHolder() {
synchronized (BaseContextHolder.class) {
if(this.context == null) {
String overrideContextLocation = System.getProperty("JSR-352-BASE-CONTEXT");
List<String> contextLocations = new ArrayList<>();
contextLocations.add("jsrBaseContext.xml");
if(overrideContextLocation != null) {
contextLocations.add(overrideContextLocation);
}
this.context = new GenericXmlApplicationContext(
contextLocations.toArray(new String[contextLocations.size()]));
}
}
}
public static BaseContextHolder getInstance() {
synchronized (BaseContextHolder.class) {
if(instance == null) {
instance = new BaseContextHolder();
}
}
return instance;
}
public ApplicationContext getContext() {
return this.context;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2017 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.
@@ -15,20 +15,19 @@
*/
package org.springframework.batch.core.jsr.configuration.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.batch.api.Batchlet;
import javax.batch.runtime.JobExecution;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.springframework.batch.core.jsr.AbstractJsrTestCase;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.xml.DefaultDocumentLoader;
@@ -37,9 +36,10 @@ import org.springframework.beans.factory.xml.DocumentLoader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.SimpleSaxErrorHandler;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* <p>
@@ -64,7 +64,7 @@ public class JsrBeanDefinitionDocumentReaderTests extends AbstractJsrTestCase {
JsrXmlApplicationContext applicationContext = new JsrXmlApplicationContext(jobParameters);
applicationContext.setValidating(false);
applicationContext.load(new ClassPathResource("baseContext.xml"),
applicationContext.load(new ClassPathResource("jsrBaseContext.xml"),
new ClassPathResource("/META-INF/batch.xml"),
new ClassPathResource("/META-INF/batch-jobs/jsrPropertyPreparseTestJob.xml"));
applicationContext.refresh();
@@ -105,7 +105,7 @@ public class JsrBeanDefinitionDocumentReaderTests extends AbstractJsrTestCase {
@SuppressWarnings("resource")
JsrXmlApplicationContext applicationContext = new JsrXmlApplicationContext(jobParameters);
applicationContext.setValidating(false);
applicationContext.load(new ClassPathResource("baseContext.xml"),
applicationContext.load(new ClassPathResource("jsrBaseContext.xml"),
new ClassPathResource("/META-INF/batch.xml"),
new ClassPathResource("/META-INF/batch-jobs/jsrPropertyPreparseTestJob.xml"));
applicationContext.refresh();
@@ -132,7 +132,7 @@ public class JsrBeanDefinitionDocumentReaderTests extends AbstractJsrTestCase {
@SuppressWarnings("resource")
JsrXmlApplicationContext applicationContext = new JsrXmlApplicationContext(jobParameters);
applicationContext.setValidating(false);
applicationContext.load(new ClassPathResource("baseContext.xml"),
applicationContext.load(new ClassPathResource("jsrBaseContext.xml"),
new ClassPathResource("/META-INF/batch.xml"),
new ClassPathResource("/META-INF/batch-jobs/jsrPropertyPreparseTestJob.xml"));
applicationContext.refresh();
@@ -155,7 +155,7 @@ public class JsrBeanDefinitionDocumentReaderTests extends AbstractJsrTestCase {
public void testGenerationOfBeanDefinitionsForMultipleReferences() throws Exception {
JsrXmlApplicationContext applicationContext = new JsrXmlApplicationContext(new Properties());
applicationContext.setValidating(false);
applicationContext.load(new ClassPathResource("baseContext.xml"),
applicationContext.load(new ClassPathResource("jsrBaseContext.xml"),
new ClassPathResource("/META-INF/batch.xml"),
new ClassPathResource("/META-INF/batch-jobs/jsrUniqueInstanceTests.xml"));
applicationContext.refresh();
@@ -209,7 +209,7 @@ public class JsrBeanDefinitionDocumentReaderTests extends AbstractJsrTestCase {
public void testGenerationOfSpringBeanDefinitionsForMultipleReferences() {
JsrXmlApplicationContext applicationContext = new JsrXmlApplicationContext(new Properties());
applicationContext.setValidating(false);
applicationContext.load(new ClassPathResource("baseContext.xml"),
applicationContext.load(new ClassPathResource("jsrBaseContext.xml"),
new ClassPathResource("/META-INF/batch-jobs/jsrSpringInstanceTests.xml"));
applicationContext.refresh();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2017 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.
@@ -15,24 +15,12 @@
*/
package org.springframework.batch.core.jsr.launch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.batch.api.AbstractBatchlet;
import javax.batch.api.Batchlet;
import javax.batch.operations.JobExecutionIsRunningException;
@@ -68,7 +56,6 @@ import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.JobRepositorySupport;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -79,6 +66,14 @@ import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class JsrJobOperatorTests extends AbstractJsrTestCase {
private JobOperator jsrJobOperator;
@@ -91,7 +86,6 @@ public class JsrJobOperatorTests extends AbstractJsrTestCase {
@Before
public void setup() throws Exception {
resetBaseContext();
MockitoAnnotations.initMocks(this);
parameterConverter = new JobParametersConverterSupport();
@@ -137,6 +131,8 @@ public class JsrJobOperatorTests extends AbstractJsrTestCase {
public void testCustomBaseContextJsrCompliant() throws Exception {
System.setProperty("JSR-352-BASE-CONTEXT", "META-INF/alternativeJsrBaseContext.xml");
ReflectionTestUtils.setField(JsrJobOperator.BaseContextHolder.class, "instance", null);
JobOperator jobOperator = BatchRuntime.getJobOperator();
Object transactionManager = ReflectionTestUtils.getField(jobOperator, "transactionManager");
@@ -175,17 +171,6 @@ public class JsrJobOperatorTests extends AbstractJsrTestCase {
System.getProperties().remove("JSR-352-BASE-CONTEXT");
}
private void resetBaseContext() throws NoSuchFieldException, IllegalAccessException {
Field instancesField = ContextSingletonBeanFactoryLocator.class.getDeclaredField("instances");
instancesField.setAccessible(true);
Field instancesModifiers = Field.class.getDeclaredField("modifiers");
instancesModifiers.setAccessible(true);
instancesModifiers.setInt(instancesField, instancesField.getModifiers() & ~Modifier.FINAL);
instancesField.set(null, new HashMap());
}
@Test
public void testDefaultTaskExecutor() throws Exception {
JsrJobOperator jsrJobOperatorImpl = (JsrJobOperator) jsrJobOperator;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2017 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.
@@ -15,24 +15,23 @@
*/
package org.springframework.batch.core.jsr.step;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.jsr.AbstractJsrTestCase;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.util.Assert;
import java.util.List;
import java.util.Properties;
import javax.batch.api.Decider;
import javax.batch.runtime.BatchRuntime;
import javax.batch.runtime.BatchStatus;
import javax.batch.runtime.JobExecution;
import javax.batch.runtime.StepExecution;
import java.util.List;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.jsr.AbstractJsrTestCase;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.util.Assert;
import static org.junit.Assert.assertEquals;
@@ -47,9 +46,7 @@ public class DecisionStepTests extends AbstractJsrTestCase {
StepExecutionCountingDecider.previousStepCount = 0;
if(jobExplorer == null) {
BeanFactoryLocator beanFactoryLocactor = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference ref = beanFactoryLocactor.useBeanFactory("baseContext");
baseContext = (ApplicationContext) ref.getFactory();
baseContext = new GenericXmlApplicationContext("jsrBaseContext.xml");
baseContext.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009 the original author or authors.
* Copyright 2009-2017 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.
@@ -15,19 +15,20 @@
*/
package org.springframework.batch.core.partition.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.ResourceArrayPropertyEditor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MultiResourcePartitionerTests {
private MultiResourcePartitioner partitioner = new MultiResourcePartitioner();
@@ -35,7 +36,7 @@ public class MultiResourcePartitionerTests {
@Before
public void setUp() {
ResourceArrayPropertyEditor editor = new ResourceArrayPropertyEditor();
editor.setAsText("classpath:baseContext.xml");
editor.setAsText("classpath:jsrBaseContext.xml");
partitioner.setResources((Resource[]) editor.getValue());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 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.
@@ -73,14 +73,14 @@ import org.springframework.util.MethodInvoker;
* <p>
* The connection returned will be a close-suppressing proxy instead of the
* physical {@link Connection}. Be aware that you will not be able to cast this
* to a native <code>OracleConnection</code> or the like anymore; you need to
* use a {@link org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor}.
* to a native <code>OracleConnection</code> or the like anymore; you'd be required to use
* {@link java.sql.Connection#unwrap(Class)}.
*
* @author Thomas Risberg
* @see #getConnection()
* @see java.sql.Connection#close()
* @see DataSourceUtils#releaseConnection
* @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor
* @see java.sql.Connection#unwrap(Class)
* @since 2.0
*/
public class ExtendedConnectionDataSourceProxy implements SmartDataSource, InitializingBean {