diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java index 9d7f54a03..50d0c42bd 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java @@ -15,6 +15,8 @@ */ package org.springframework.batch.core.step.tasklet; +import java.beans.PropertyEditor; +import java.util.List; import java.util.concurrent.Semaphore; import org.apache.commons.logging.Log; @@ -46,7 +48,9 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttribute; +import org.springframework.transaction.interceptor.TransactionAttributeEditor; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Simple implementation of executing the step as a call to a {@link Tasklet}, @@ -126,6 +130,20 @@ public class TaskletStep extends AbstractStep { this.transactionManager = transactionManager; } + /** + * Public setter for the {@link TransactionAttribute}. + * + * @param transactionAttributeList A list of all the transaction attributes + * to set + */ + public void setTransactionAttributeList(List transactionAttributeList) { + String[] stringArray = transactionAttributeList.toArray(new String[0]); + String attributeString = StringUtils.arrayToCommaDelimitedString(stringArray); + PropertyEditor editor = new TransactionAttributeEditor(); + editor.setAsText(attributeString); + this.setTransactionAttribute((TransactionAttribute) editor.getValue()); + } + /** * Public setter for the {@link TransactionAttribute}. * diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java index 0c262cf83..88b0d7e75 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java @@ -32,9 +32,13 @@ import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.batch.repeat.CompletionPolicy; import org.springframework.batch.repeat.policy.SimpleCompletionPolicy; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; +import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.interceptor.RollbackRuleAttribute; +import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; /** * @author Thomas Risberg @@ -134,63 +138,78 @@ public class StepParserTests { "org/springframework/batch/core/configuration/xml/StepParserParentAndRefTests-context.xml"); } - @SuppressWarnings("unchecked") @Test - public void testParentOnInlineStep() throws Exception { - ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( + public void testParentStep() throws Exception { + ApplicationContext ctx = new ClassPathXmlApplicationContext( "org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml"); - Map beans = ctx.getBeansOfType(Step.class); - assertTrue(beans.containsKey("s1")); - Step s1 = (Step) ctx.getBean("s1"); - assertTrue(s1 instanceof TaskletStep); - assertTrue(getListener((TaskletStep) s1) instanceof StepExecutionListenerSupport); + + // Inline Step + assertTrue(getListener("s1", ctx) instanceof StepExecutionListenerSupport); + + // Standalone Step + assertTrue(getListener("s2", ctx) instanceof StepExecutionListenerSupport); + + // Inline With Tasklet Attribute Step + assertTrue(getListener("s3", ctx) instanceof StepExecutionListenerSupport); + + // Standalone With Tasklet Attribute Step + assertTrue(getListener("s4", ctx) instanceof StepExecutionListenerSupport); } - @SuppressWarnings("unchecked") @Test - public void testParentOnStandaloneStep() throws Exception { - ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( + public void testInheritTransactionAttributes() throws Exception { + ApplicationContext ctx = new ClassPathXmlApplicationContext( "org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml"); - Map beans = ctx.getBeansOfType(Step.class); - assertTrue(beans.containsKey("s2")); - Step s2 = (Step) ctx.getBean("s2"); - assertTrue(s2 instanceof DelegatingStep); - assertTrue(getListener((DelegatingStep) s2) instanceof StepExecutionListenerSupport); + + // On Inline - No Merge + validateTransactionAttributesInherited("s1", false, ctx); + + // On Standalone - No Merge + validateTransactionAttributesInherited("s2", false, ctx); + + // On Inline With Tasklet Ref - No Merge + validateTransactionAttributesInherited("s3", false, ctx); + + // On Standalone With Tasklet Ref - No Merge + validateTransactionAttributesInherited("s4", false, ctx); + + // On Inline + validateTransactionAttributesInherited("s5", true, ctx); + + // On Standalone + validateTransactionAttributesInherited("s6", true, ctx); + + // On Inline With Tasklet Ref + validateTransactionAttributesInherited("s7", true, ctx); + + // On Standalone With Tasklet Ref + validateTransactionAttributesInherited("s8", true, ctx); + } + + private void validateTransactionAttributesInherited(String stepName, boolean inherited, ApplicationContext ctx) { + RuleBasedTransactionAttribute txa = getTransactionAttribute(ctx, stepName); + assertEquals(TransactionDefinition.PROPAGATION_REQUIRED, txa.getPropagationBehavior()); + assertEquals(TransactionDefinition.ISOLATION_DEFAULT, txa.getIsolationLevel()); + if (inherited) { + assertEquals(10, txa.getTimeout()); + RollbackRuleAttribute rra = (RollbackRuleAttribute) txa.getRollbackRules().get(0); + assertEquals("org.springframework.dao.DataIntegrityViolationException", rra.getExceptionName()); + } + else { + assertTrue(10 != txa.getTimeout()); + assertTrue(txa.getRollbackRules().isEmpty()); + } } @SuppressWarnings("unchecked") - @Test - public void testParentOnInlineWithTaskletAttributeStep() throws Exception { - ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( - "org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml"); + private StepExecutionListener getListener(String stepName, ApplicationContext ctx) throws Exception { Map beans = ctx.getBeansOfType(Step.class); - assertTrue(beans.containsKey("s3")); - Step s3 = (Step) ctx.getBean("s3"); - assertTrue(s3 instanceof TaskletStep); - assertTrue(getListener((TaskletStep) s3) instanceof StepExecutionListenerSupport); - } - - @SuppressWarnings("unchecked") - @Test - public void testParentOnStandaloneWithTaskletAttributeStep() throws Exception { - ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( - "org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml"); - Map beans = ctx.getBeansOfType(Step.class); - assertTrue(beans.containsKey("s4")); - Step s4 = (Step) ctx.getBean("s4"); - assertTrue(s4 instanceof DelegatingStep); - assertTrue(getListener((DelegatingStep) s4) instanceof StepExecutionListenerSupport); - } - - private StepExecutionListener getListener(DelegatingStep step) throws Exception { - assertTrue(step instanceof DelegatingStep); - Object delegate = ReflectionTestUtils.getField(step, "delegate"); - assertTrue(delegate instanceof TaskletStep); - return getListener((TaskletStep) delegate); - } - - @SuppressWarnings("unchecked") - private StepExecutionListener getListener(TaskletStep step) throws Exception { + assertTrue(beans.containsKey(stepName)); + Step step = (Step) ctx.getBean(stepName); + if (step instanceof DelegatingStep) { + step = (Step) ReflectionTestUtils.getField(step, "delegate"); + } + assertTrue(step instanceof TaskletStep); Object compositeListener = ReflectionTestUtils.getField(step, "stepExecutionListener"); Object composite = ReflectionTestUtils.getField(compositeListener, "list"); List list = (List) ReflectionTestUtils @@ -203,4 +222,19 @@ public class StepParserTests { } return listener; } + + @SuppressWarnings("unchecked") + private RuleBasedTransactionAttribute getTransactionAttribute(ApplicationContext ctx, String stepName) { + Map beans = ctx.getBeansOfType(Step.class); + assertTrue(beans.containsKey(stepName)); + Step step = (Step) ctx.getBean(stepName); + if (step instanceof DelegatingStep) { + step = (Step) ReflectionTestUtils.getField(step, "delegate"); + } + assertTrue(step instanceof TaskletStep); + Object transactionAttribute = ReflectionTestUtils.getField(step, "transactionAttribute"); + RuleBasedTransactionAttribute txa = (RuleBasedTransactionAttribute) transactionAttribute; + return txa; + } + } diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml index 7268ee0af..cdf07e362 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml @@ -9,19 +9,47 @@ + PROPAGATION_REQUIRED,ISOLATION_DEFAULT - - + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT + + + + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT + + + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT + + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT - + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT + + + + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT + + + + PROPAGATION_REQUIRED,ISOLATION_DEFAULT + + + timeout_10 + -org.springframework.dao.DataIntegrityViolationException +