Use InheritedThreadLocal (as interim measure) so that child threads are aware of step context
This commit is contained in:
@@ -30,11 +30,12 @@ import org.springframework.core.Ordered;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepScope implements Scope,
|
||||
BeanFactoryPostProcessor, Ordered {
|
||||
public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered {
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private Object mutex = new Object();
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
@@ -59,12 +60,16 @@ public class StepScope implements Scope,
|
||||
SimpleStepContext context = getContext();
|
||||
Object scopedObject = context.getAttribute(name);
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
context.setAttribute(name, scopedObject);
|
||||
if (scopedObject instanceof StepContextAware) {
|
||||
((StepContextAware) scopedObject).setStepContext(context);
|
||||
synchronized (mutex) {
|
||||
scopedObject = context.getAttribute(name);
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
context.setAttribute(name, scopedObject);
|
||||
if (scopedObject instanceof StepContextAware) {
|
||||
((StepContextAware) scopedObject).setStepContext(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return scopedObject;
|
||||
}
|
||||
@@ -132,7 +137,7 @@ public class StepScope implements Scope,
|
||||
|
||||
/**
|
||||
* Public setter for the name property. This can then be used as a bean
|
||||
* definition attribute, e.g. scope="step". Defaults to "step".
|
||||
* definition attribute, e.g. scope="step". Defaults to "step".
|
||||
*
|
||||
* @param name
|
||||
* the name to set for this scope.
|
||||
|
||||
@@ -21,13 +21,13 @@ package org.springframework.batch.execution.scope;
|
||||
*/
|
||||
public class StepSynchronizationManager {
|
||||
|
||||
private static final ThreadLocal contextHolder = new ThreadLocal();
|
||||
private static final ThreadLocal contextHolder = new InheritableThreadLocal();
|
||||
|
||||
/**
|
||||
* Getter for the current context..
|
||||
*
|
||||
* @return the current {@link SimpleStepContext} or null if there is none (if
|
||||
* we are not in a step).
|
||||
* @return the current {@link SimpleStepContext} or null if there is none
|
||||
* (if we are not in a step).
|
||||
*/
|
||||
public static SimpleStepContext getContext() {
|
||||
return (SimpleStepContext) contextHolder.get();
|
||||
@@ -56,7 +56,7 @@ public class StepSynchronizationManager {
|
||||
*/
|
||||
public static StepContext close() {
|
||||
SimpleStepContext oldSession = getContext();
|
||||
if (oldSession==null) {
|
||||
if (oldSession == null) {
|
||||
return null;
|
||||
}
|
||||
oldSession.close();
|
||||
|
||||
@@ -77,12 +77,84 @@ public class StepContextAwareStepScopeTests extends TestCase {
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
public void testScopedBeanWithProxy() throws Exception {
|
||||
StepContext context = StepSynchronizationManager.open();
|
||||
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass());
|
||||
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
|
||||
assertNotNull(bean);
|
||||
// A scoped proxy is only accessible through public methods
|
||||
assertEquals(null, bean.name);
|
||||
assertEquals("spam", bean.getName());
|
||||
assertEquals(context, bean.getContext());
|
||||
}
|
||||
|
||||
public void testScopedBeanWithProxyInThread() throws Exception {
|
||||
StepSynchronizationManager.open();
|
||||
final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass());
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
|
||||
list.add(bean.getName());
|
||||
}
|
||||
}).start();
|
||||
int count = 0;
|
||||
while(list.size()==0 && count++ <10) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
if (list.size()==0) {
|
||||
fail("Scoped proxy was not created in child thread - maybe we need to use InheritableThreadLocal?");
|
||||
}
|
||||
String name = (String) list.get(0);
|
||||
assertEquals("spam", name);
|
||||
}
|
||||
|
||||
public void testScopedBeanWithTwoProxiesInThreads() throws Exception {
|
||||
StepSynchronizationManager.open();
|
||||
final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass());
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
|
||||
int count = 0;
|
||||
while(list.size()==0 && count++ <10) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
fail("Timeout waiting for other thread to add a bean to list.");
|
||||
}
|
||||
}
|
||||
bean.getName();
|
||||
list.add(bean);
|
||||
}
|
||||
}).start();
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
|
||||
bean.getName();
|
||||
list.add(bean);
|
||||
}
|
||||
}).start();
|
||||
int count = 0;
|
||||
while(list.size()<2 && count++ <10) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
if (list.size()<2) {
|
||||
fail("Scoped proxies were not created in child threads");
|
||||
}
|
||||
TestBeanAware bean1 = (TestBeanAware) list.get(0);
|
||||
TestBeanAware bean2 = (TestBeanAware) list.get(1);
|
||||
assertEquals("spam", bean1.getName());
|
||||
assertSame(bean1.getLock(), bean2.getLock());
|
||||
}
|
||||
|
||||
public static class TestBean {
|
||||
String name;
|
||||
TestBean child;
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setChild(TestBean child) {
|
||||
this.child = child;
|
||||
}
|
||||
@@ -93,8 +165,15 @@ public class StepContextAwareStepScopeTests extends TestCase {
|
||||
|
||||
public static class TestBeanAware extends TestBean implements StepContextAware {
|
||||
AttributeAccessor context;
|
||||
Object lock = new Object();
|
||||
public void setStepContext(StepContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
public AttributeAccessor getContext() {
|
||||
return context;
|
||||
}
|
||||
public Object getLock() {
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,11 @@
|
||||
<property name="name" value="bar" />
|
||||
</bean>
|
||||
|
||||
<bean id="proxy"
|
||||
class="org.springframework.batch.execution.scope.StepContextAwareStepScopeTests$TestBeanAware"
|
||||
scope="step">
|
||||
<aop:scoped-proxy/>
|
||||
<property name="name" value="spam" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user