diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepContext.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepContext.java index ed5f61435..66201ec92 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepContext.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepContext.java @@ -1,310 +1,310 @@ -/* - * Copyright 2006-2018 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.scope.context; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.Set; - -import org.springframework.batch.core.JobInstance; -import org.springframework.batch.core.JobParameter; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.core.UnexpectedJobExecutionException; -import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; -import org.springframework.batch.core.scope.StepScope; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.repeat.context.SynchronizedAttributeAccessor; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * A context object that can be used to interrogate the current - * {@link StepExecution} and some of its associated properties using expressions - * based on bean paths. Has public getters for the step execution and - * convenience methods for accessing commonly used properties like the - * {@link ExecutionContext} associated with the step or its enclosing job - * execution. - * - * @author Dave Syer - * @author Michael Minella - * @author Mahmoud Ben Hassine - * @author Nicolas Widart - * - */ -public class StepContext extends SynchronizedAttributeAccessor { - - private StepExecution stepExecution; - - private Map> callbacks = new HashMap<>(); - - private BatchPropertyContext propertyContext = null; - - /** - * Create a new instance of {@link StepContext} for this - * {@link StepExecution}. - * - * @param stepExecution a step execution - */ - public StepContext(StepExecution stepExecution) { - super(); - Assert.notNull(stepExecution, "A StepContext must have a non-null StepExecution"); - this.stepExecution = stepExecution; - } - - public StepContext(StepExecution stepExecution, BatchPropertyContext propertyContext) { - super(); - Assert.notNull(stepExecution, "A StepContext must have a non-null StepExecution"); - this.stepExecution = stepExecution; - this.propertyContext = propertyContext; - } - - /** - * Convenient accessor for current step name identifier. Usually this is the - * same as the bean name of the step that is executing (but might not be - * e.g. in a partition). - * - * @return the step name identifier of the current {@link StepExecution} - */ - public String getStepName() { - return stepExecution.getStepName(); - } - - /** - * Convenient accessor for current job name identifier. - * - * @return the job name identifier of the enclosing {@link JobInstance} - * associated with the current {@link StepExecution} - */ - public String getJobName() { - Assert.state(stepExecution.getJobExecution() != null, "StepExecution does not have a JobExecution"); - Assert.state(stepExecution.getJobExecution().getJobInstance() != null, - "StepExecution does not have a JobInstance"); - return stepExecution.getJobExecution().getJobInstance().getJobName(); - } - - /** - * Convenient accessor for current job identifier. - * - * @return the job identifier of the enclosing {@link JobInstance} - * associated with the current {@link StepExecution} - */ - public Long getJobId() { - Assert.state(stepExecution.getJobExecution() != null, "StepExecution does not have a JobExecution"); - Assert.state(stepExecution.getJobExecution().getJobInstance() != null, - "StepExecution does not have a JobInstance"); - return stepExecution.getJobExecution().getJobInstance().getId(); - } - - /** - * Convenient accessor for System properties to make it easy to access them - * from placeholder expressions. - * - * @return the current System properties - */ - public Properties getSystemProperties() { - return System.getProperties(); - } - - /** - * @return a map containing the items from the step {@link ExecutionContext} - */ - public Map getStepExecutionContext() { - Map result = new HashMap<>(); - for (Entry entry : stepExecution.getExecutionContext().entrySet()) { - result.put(entry.getKey(), entry.getValue()); - } - return Collections.unmodifiableMap(result); - } - - /** - * @return a map containing the items from the job {@link ExecutionContext} - */ - public Map getJobExecutionContext() { - Map result = new HashMap<>(); - for (Entry entry : stepExecution.getJobExecution().getExecutionContext().entrySet()) { - result.put(entry.getKey(), entry.getValue()); - } - return Collections.unmodifiableMap(result); - } - - /** - * @return a map containing the items from the {@link JobParameters} - */ - public Map getJobParameters() { - Map result = new HashMap<>(); - for (Entry entry : stepExecution.getJobParameters().getParameters().entrySet()) { - result.put(entry.getKey(), entry.getValue().getValue()); - } - return Collections.unmodifiableMap(result); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public Map getPartitionPlan() { - Map partitionPlanProperties = new HashMap<>(); - - if(propertyContext != null) { - Map partitionProperties = propertyContext.getStepProperties(getStepName()); - partitionPlanProperties = partitionProperties; - } - - return Collections.unmodifiableMap(partitionPlanProperties); - } - - /** - * Allow clients to register callbacks for clean up on close. - * - * @param name the callback id (unique attribute key in this context) - * @param callback a callback to execute on close - */ - public void registerDestructionCallback(String name, Runnable callback) { - synchronized (callbacks) { - Set set = callbacks.get(name); - if (set == null) { - set = new HashSet<>(); - callbacks.put(name, set); - } - set.add(callback); - } - } - - private void unregisterDestructionCallbacks(String name) { - synchronized (callbacks) { - callbacks.remove(name); - } - } - - /** - * Override base class behaviour to ensure destruction callbacks are - * unregistered as well as the default behaviour. - * - * @see SynchronizedAttributeAccessor#removeAttribute(String) - */ - @Override - @Nullable - public Object removeAttribute(String name) { - unregisterDestructionCallbacks(name); - return super.removeAttribute(name); - } - - /** - * Clean up the context at the end of a step execution. Must be called once - * at the end of a step execution to honour the destruction callback - * contract from the {@link StepScope}. - */ - public void close() { - - List errors = new ArrayList<>(); - - Map> copy = Collections.unmodifiableMap(callbacks); - - for (Entry> entry : copy.entrySet()) { - Set set = entry.getValue(); - for (Runnable callback : set) { - if (callback != null) { - /* - * The documentation of the interface says that these - * callbacks must not throw exceptions, but we don't trust - * them necessarily... - */ - try { - callback.run(); - } - catch (RuntimeException t) { - errors.add(t); - } - } - } - } - - if (errors.isEmpty()) { - return; - } - - Exception error = errors.get(0); - if (error instanceof RuntimeException) { - throw (RuntimeException) error; - } - else { - throw new UnexpectedJobExecutionException("Could not close step context, rethrowing first of " - + errors.size() + " exceptions.", error); - } - } - - /** - * The current {@link StepExecution} that is active in this context. - * - * @return the current {@link StepExecution} - */ - public StepExecution getStepExecution() { - return stepExecution; - } - - /** - * @return unique identifier for this context based on the step execution - */ - public String getId() { - Assert.state(stepExecution.getId() != null, "StepExecution has no id. " - + "It must be saved before it can be used in step scope."); - return "execution#" + stepExecution.getId(); - } - - /** - * Extend the base class method to include the step execution itself as a - * key (i.e. two contexts are only equal if their step executions are the - * same). - * - * @see SynchronizedAttributeAccessor#equals(Object) - */ - @Override - public boolean equals(Object other) { - if (!(other instanceof StepContext)) { - return false; - } - if (other == this) { - return true; - } - StepContext context = (StepContext) other; - if (context.stepExecution == stepExecution) { - return true; - } - return stepExecution.equals(context.stepExecution); - } - - /** - * Overrides the default behaviour to provide a hash code based only on the - * step execution. - * - * @see SynchronizedAttributeAccessor#hashCode() - */ - @Override - public int hashCode() { - return stepExecution.hashCode(); - } - - @Override - public String toString() { - return super.toString() + ", stepExecutionContext=" + getStepExecutionContext() + ", jobExecutionContext=" - + getJobExecutionContext() + ", jobParameters=" + getJobParameters(); - } - -} +/* + * Copyright 2006-2019 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.scope.context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import org.springframework.batch.core.JobInstance; +import org.springframework.batch.core.JobParameter; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.UnexpectedJobExecutionException; +import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; +import org.springframework.batch.core.scope.StepScope; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.repeat.context.SynchronizedAttributeAccessor; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * A context object that can be used to interrogate the current + * {@link StepExecution} and some of its associated properties using expressions + * based on bean paths. Has public getters for the step execution and + * convenience methods for accessing commonly used properties like the + * {@link ExecutionContext} associated with the step or its enclosing job + * execution. + * + * @author Dave Syer + * @author Michael Minella + * @author Mahmoud Ben Hassine + * @author Nicolas Widart + * + */ +public class StepContext extends SynchronizedAttributeAccessor { + + private StepExecution stepExecution; + + private Map> callbacks = new HashMap<>(); + + private BatchPropertyContext propertyContext = null; + + /** + * Create a new instance of {@link StepContext} for this + * {@link StepExecution}. + * + * @param stepExecution a step execution + */ + public StepContext(StepExecution stepExecution) { + super(); + Assert.notNull(stepExecution, "A StepContext must have a non-null StepExecution"); + this.stepExecution = stepExecution; + } + + public StepContext(StepExecution stepExecution, BatchPropertyContext propertyContext) { + super(); + Assert.notNull(stepExecution, "A StepContext must have a non-null StepExecution"); + this.stepExecution = stepExecution; + this.propertyContext = propertyContext; + } + + /** + * Convenient accessor for current step name identifier. Usually this is the + * same as the bean name of the step that is executing (but might not be + * e.g. in a partition). + * + * @return the step name identifier of the current {@link StepExecution} + */ + public String getStepName() { + return stepExecution.getStepName(); + } + + /** + * Convenient accessor for current job name identifier. + * + * @return the job name identifier of the enclosing {@link JobInstance} + * associated with the current {@link StepExecution} + */ + public String getJobName() { + Assert.state(stepExecution.getJobExecution() != null, "StepExecution does not have a JobExecution"); + Assert.state(stepExecution.getJobExecution().getJobInstance() != null, + "StepExecution does not have a JobInstance"); + return stepExecution.getJobExecution().getJobInstance().getJobName(); + } + + /** + * Convenient accessor for current {@link JobInstance} identifier. + * + * @return the identifier of the enclosing {@link JobInstance} + * associated with the current {@link StepExecution} + */ + public Long getJobInstanceId() { + Assert.state(stepExecution.getJobExecution() != null, "StepExecution does not have a JobExecution"); + Assert.state(stepExecution.getJobExecution().getJobInstance() != null, + "StepExecution does not have a JobInstance"); + return stepExecution.getJobExecution().getJobInstance().getInstanceId(); + } + + /** + * Convenient accessor for System properties to make it easy to access them + * from placeholder expressions. + * + * @return the current System properties + */ + public Properties getSystemProperties() { + return System.getProperties(); + } + + /** + * @return a map containing the items from the step {@link ExecutionContext} + */ + public Map getStepExecutionContext() { + Map result = new HashMap<>(); + for (Entry entry : stepExecution.getExecutionContext().entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + return Collections.unmodifiableMap(result); + } + + /** + * @return a map containing the items from the job {@link ExecutionContext} + */ + public Map getJobExecutionContext() { + Map result = new HashMap<>(); + for (Entry entry : stepExecution.getJobExecution().getExecutionContext().entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + return Collections.unmodifiableMap(result); + } + + /** + * @return a map containing the items from the {@link JobParameters} + */ + public Map getJobParameters() { + Map result = new HashMap<>(); + for (Entry entry : stepExecution.getJobParameters().getParameters().entrySet()) { + result.put(entry.getKey(), entry.getValue().getValue()); + } + return Collections.unmodifiableMap(result); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Map getPartitionPlan() { + Map partitionPlanProperties = new HashMap<>(); + + if(propertyContext != null) { + Map partitionProperties = propertyContext.getStepProperties(getStepName()); + partitionPlanProperties = partitionProperties; + } + + return Collections.unmodifiableMap(partitionPlanProperties); + } + + /** + * Allow clients to register callbacks for clean up on close. + * + * @param name the callback id (unique attribute key in this context) + * @param callback a callback to execute on close + */ + public void registerDestructionCallback(String name, Runnable callback) { + synchronized (callbacks) { + Set set = callbacks.get(name); + if (set == null) { + set = new HashSet<>(); + callbacks.put(name, set); + } + set.add(callback); + } + } + + private void unregisterDestructionCallbacks(String name) { + synchronized (callbacks) { + callbacks.remove(name); + } + } + + /** + * Override base class behaviour to ensure destruction callbacks are + * unregistered as well as the default behaviour. + * + * @see SynchronizedAttributeAccessor#removeAttribute(String) + */ + @Override + @Nullable + public Object removeAttribute(String name) { + unregisterDestructionCallbacks(name); + return super.removeAttribute(name); + } + + /** + * Clean up the context at the end of a step execution. Must be called once + * at the end of a step execution to honour the destruction callback + * contract from the {@link StepScope}. + */ + public void close() { + + List errors = new ArrayList<>(); + + Map> copy = Collections.unmodifiableMap(callbacks); + + for (Entry> entry : copy.entrySet()) { + Set set = entry.getValue(); + for (Runnable callback : set) { + if (callback != null) { + /* + * The documentation of the interface says that these + * callbacks must not throw exceptions, but we don't trust + * them necessarily... + */ + try { + callback.run(); + } + catch (RuntimeException t) { + errors.add(t); + } + } + } + } + + if (errors.isEmpty()) { + return; + } + + Exception error = errors.get(0); + if (error instanceof RuntimeException) { + throw (RuntimeException) error; + } + else { + throw new UnexpectedJobExecutionException("Could not close step context, rethrowing first of " + + errors.size() + " exceptions.", error); + } + } + + /** + * The current {@link StepExecution} that is active in this context. + * + * @return the current {@link StepExecution} + */ + public StepExecution getStepExecution() { + return stepExecution; + } + + /** + * @return unique identifier for this context based on the step execution + */ + public String getId() { + Assert.state(stepExecution.getId() != null, "StepExecution has no id. " + + "It must be saved before it can be used in step scope."); + return "execution#" + stepExecution.getId(); + } + + /** + * Extend the base class method to include the step execution itself as a + * key (i.e. two contexts are only equal if their step executions are the + * same). + * + * @see SynchronizedAttributeAccessor#equals(Object) + */ + @Override + public boolean equals(Object other) { + if (!(other instanceof StepContext)) { + return false; + } + if (other == this) { + return true; + } + StepContext context = (StepContext) other; + if (context.stepExecution == stepExecution) { + return true; + } + return stepExecution.equals(context.stepExecution); + } + + /** + * Overrides the default behaviour to provide a hash code based only on the + * step execution. + * + * @see SynchronizedAttributeAccessor#hashCode() + */ + @Override + public int hashCode() { + return stepExecution.hashCode(); + } + + @Override + public String toString() { + return super.toString() + ", stepExecutionContext=" + getStepExecutionContext() + ", jobExecutionContext=" + + getJobExecutionContext() + ", jobParameters=" + getJobParameters(); + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepContextTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepContextTests.java index 8628512d1..dbcf9a4c2 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepContextTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepContextTests.java @@ -1,213 +1,214 @@ -/* - * Copyright 2006-2018 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.scope.context; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.junit.Test; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobInstance; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; -import org.springframework.batch.item.ExecutionContext; - -/** - * @author Dave Syer - * @author Nicolas Widart - * - */ -public class StepContextTests { - - private List list = new ArrayList<>(); - - private StepExecution stepExecution = new StepExecution("step", new JobExecution(new JobInstance(2L, "job"), 0L, null, null), 1L); - - private StepContext context = new StepContext(stepExecution); - - private BatchPropertyContext propertyContext = new BatchPropertyContext(); - - @Test - public void testGetStepExecution() { - context = new StepContext(stepExecution); - assertNotNull(context.getStepExecution()); - } - - @Test - public void testNullStepExecution() { - try { - context = new StepContext(null); - fail("Expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) { - // expected - } - } - - @Test - public void testGetPartitionPlan() { - Properties partitionPropertyValues = new Properties(); - partitionPropertyValues.put("key1", "value1"); - - propertyContext.setStepProperties(stepExecution.getStepName(), partitionPropertyValues); - - context = new StepContext(stepExecution, propertyContext); - - Map plan = context.getPartitionPlan(); - assertEquals("value1", plan.get("key1")); - } - - @Test - public void testEqualsSelf() { - assertEquals(context, context); - } - - @Test - public void testNotEqualsNull() { - assertFalse(context.equals(null)); - } - - @Test - public void testEqualsContextWithSameStepExecution() { - assertEquals(new StepContext(stepExecution), context); - } - - @Test - public void testDestructionCallbackSunnyDay() throws Exception { - context.setAttribute("foo", "FOO"); - context.registerDestructionCallback("foo", new Runnable() { - @Override - public void run() { - list.add("bar"); - } - }); - context.close(); - assertEquals(1, list.size()); - assertEquals("bar", list.get(0)); - } - - @Test - public void testDestructionCallbackMissingAttribute() throws Exception { - context.registerDestructionCallback("foo", new Runnable() { - @Override - public void run() { - list.add("bar"); - } - }); - context.close(); - // Yes the callback should be called even if the attribute is missing - - // for inner beans - assertEquals(1, list.size()); - } - - @Test - public void testDestructionCallbackWithException() throws Exception { - context.setAttribute("foo", "FOO"); - context.setAttribute("bar", "BAR"); - context.registerDestructionCallback("bar", new Runnable() { - @Override - public void run() { - list.add("spam"); - throw new RuntimeException("fail!"); - } - }); - context.registerDestructionCallback("foo", new Runnable() { - @Override - public void run() { - list.add("bar"); - throw new RuntimeException("fail!"); - } - }); - try { - context.close(); - fail("Expected RuntimeException"); - } - catch (RuntimeException e) { - // We don't care which one was thrown... - assertEquals("fail!", e.getMessage()); - } - // ...but we do care that both were executed: - assertEquals(2, list.size()); - assertTrue(list.contains("bar")); - assertTrue(list.contains("spam")); - } - - @Test - public void testStepName() throws Exception { - assertEquals("step", context.getStepName()); - } - - @Test - public void testJobName() throws Exception { - assertEquals("job", context.getJobName()); - } - - @Test - public void testJobId() throws Exception { - assertEquals(2L, (long)context.getJobId()); - } - - @Test - public void testStepExecutionContext() throws Exception { - ExecutionContext executionContext = stepExecution.getExecutionContext(); - executionContext.put("foo", "bar"); - assertEquals("bar", context.getStepExecutionContext().get("foo")); - } - - @Test - public void testSystemProperties() throws Exception { - System.setProperty("foo", "bar"); - assertEquals("bar", context.getSystemProperties().getProperty("foo")); - } - - @Test - public void testJobExecutionContext() throws Exception { - ExecutionContext executionContext = stepExecution.getJobExecution().getExecutionContext(); - executionContext.put("foo", "bar"); - assertEquals("bar", context.getJobExecutionContext().get("foo")); - } - - @Test - public void testJobParameters() throws Exception { - JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters(); - JobInstance instance = stepExecution.getJobExecution().getJobInstance(); - stepExecution = new StepExecution("step", new JobExecution(instance, jobParameters)); - context = new StepContext(stepExecution); - assertEquals("bar", context.getJobParameters().get("foo")); - } - - @Test - public void testContextId() throws Exception { - assertEquals("execution#1", context.getId()); - } - - @Test(expected = IllegalStateException.class) - public void testIllegalContextId() throws Exception { - context = new StepContext(new StepExecution("foo", new JobExecution(0L))); - context.getId(); - } - -} +/* + * Copyright 2006-2019 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.scope.context; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.junit.Test; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobInstance; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; +import org.springframework.batch.item.ExecutionContext; + +/** + * @author Dave Syer + * @author Nicolas Widart + * @author Mahmoud Ben Hassine + * + */ +public class StepContextTests { + + private List list = new ArrayList<>(); + + private StepExecution stepExecution = new StepExecution("step", new JobExecution(new JobInstance(2L, "job"), 0L, null, null), 1L); + + private StepContext context = new StepContext(stepExecution); + + private BatchPropertyContext propertyContext = new BatchPropertyContext(); + + @Test + public void testGetStepExecution() { + context = new StepContext(stepExecution); + assertNotNull(context.getStepExecution()); + } + + @Test + public void testNullStepExecution() { + try { + context = new StepContext(null); + fail("Expected IllegalArgumentException"); + } + catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testGetPartitionPlan() { + Properties partitionPropertyValues = new Properties(); + partitionPropertyValues.put("key1", "value1"); + + propertyContext.setStepProperties(stepExecution.getStepName(), partitionPropertyValues); + + context = new StepContext(stepExecution, propertyContext); + + Map plan = context.getPartitionPlan(); + assertEquals("value1", plan.get("key1")); + } + + @Test + public void testEqualsSelf() { + assertEquals(context, context); + } + + @Test + public void testNotEqualsNull() { + assertFalse(context.equals(null)); + } + + @Test + public void testEqualsContextWithSameStepExecution() { + assertEquals(new StepContext(stepExecution), context); + } + + @Test + public void testDestructionCallbackSunnyDay() throws Exception { + context.setAttribute("foo", "FOO"); + context.registerDestructionCallback("foo", new Runnable() { + @Override + public void run() { + list.add("bar"); + } + }); + context.close(); + assertEquals(1, list.size()); + assertEquals("bar", list.get(0)); + } + + @Test + public void testDestructionCallbackMissingAttribute() throws Exception { + context.registerDestructionCallback("foo", new Runnable() { + @Override + public void run() { + list.add("bar"); + } + }); + context.close(); + // Yes the callback should be called even if the attribute is missing - + // for inner beans + assertEquals(1, list.size()); + } + + @Test + public void testDestructionCallbackWithException() throws Exception { + context.setAttribute("foo", "FOO"); + context.setAttribute("bar", "BAR"); + context.registerDestructionCallback("bar", new Runnable() { + @Override + public void run() { + list.add("spam"); + throw new RuntimeException("fail!"); + } + }); + context.registerDestructionCallback("foo", new Runnable() { + @Override + public void run() { + list.add("bar"); + throw new RuntimeException("fail!"); + } + }); + try { + context.close(); + fail("Expected RuntimeException"); + } + catch (RuntimeException e) { + // We don't care which one was thrown... + assertEquals("fail!", e.getMessage()); + } + // ...but we do care that both were executed: + assertEquals(2, list.size()); + assertTrue(list.contains("bar")); + assertTrue(list.contains("spam")); + } + + @Test + public void testStepName() throws Exception { + assertEquals("step", context.getStepName()); + } + + @Test + public void testJobName() throws Exception { + assertEquals("job", context.getJobName()); + } + + @Test + public void testJobInstanceId() throws Exception { + assertEquals(2L, (long)context.getJobInstanceId()); + } + + @Test + public void testStepExecutionContext() throws Exception { + ExecutionContext executionContext = stepExecution.getExecutionContext(); + executionContext.put("foo", "bar"); + assertEquals("bar", context.getStepExecutionContext().get("foo")); + } + + @Test + public void testSystemProperties() throws Exception { + System.setProperty("foo", "bar"); + assertEquals("bar", context.getSystemProperties().getProperty("foo")); + } + + @Test + public void testJobExecutionContext() throws Exception { + ExecutionContext executionContext = stepExecution.getJobExecution().getExecutionContext(); + executionContext.put("foo", "bar"); + assertEquals("bar", context.getJobExecutionContext().get("foo")); + } + + @Test + public void testJobParameters() throws Exception { + JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters(); + JobInstance instance = stepExecution.getJobExecution().getJobInstance(); + stepExecution = new StepExecution("step", new JobExecution(instance, jobParameters)); + context = new StepContext(stepExecution); + assertEquals("bar", context.getJobParameters().get("foo")); + } + + @Test + public void testContextId() throws Exception { + assertEquals("execution#1", context.getId()); + } + + @Test(expected = IllegalStateException.class) + public void testIllegalContextId() throws Exception { + context = new StepContext(new StepExecution("foo", new JobExecution(0L))); + context.getId(); + } + +}