EMMA Coverage Report (generated Thu Jan 24 13:37:04 CST 2013)
[all classes][org.springframework.batch.core.scope.context]

COVERAGE SUMMARY FOR SOURCE FILE [StepContext.java]

nameclass, %method, %block, %line, %
StepContext.java100% (1/1)94%  (15/16)89%  (309/347)94%  (62.2/66)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class StepContext100% (1/1)94%  (15/16)89%  (309/347)94%  (62.2/66)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
unregisterDestructionCallbacks (String): void 100% (1/1)74%  (14/19)93%  (3.7/4)
equals (Object): boolean 100% (1/1)77%  (20/26)88%  (7/8)
close (): void 100% (1/1)79%  (60/76)94%  (16/17)
registerDestructionCallback (String, Runnable): void 100% (1/1)86%  (31/36)95%  (7.6/8)
getJobName (): String 100% (1/1)92%  (23/25)95%  (2.9/3)
StepContext (StepExecution): void 100% (1/1)100% (14/14)100% (5/5)
getId (): String 100% (1/1)100% (20/20)100% (2/2)
getJobExecutionContext (): Map 100% (1/1)100% (29/29)100% (4/4)
getJobParameters (): Map 100% (1/1)100% (31/31)100% (4/4)
getStepExecution (): StepExecution 100% (1/1)100% (3/3)100% (1/1)
getStepExecutionContext (): Map 100% (1/1)100% (28/28)100% (4/4)
getStepName (): String 100% (1/1)100% (4/4)100% (1/1)
getSystemProperties (): Properties 100% (1/1)100% (2/2)100% (1/1)
removeAttribute (String): Object 100% (1/1)100% (7/7)100% (2/2)
toString (): String 100% (1/1)100% (23/23)100% (1/1)

1/*
2 * Copyright 2006-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.springframework.batch.core.scope.context;
17 
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.List;
23import java.util.Map;
24import java.util.Properties;
25import java.util.Set;
26import java.util.Map.Entry;
27 
28import org.springframework.batch.core.JobInstance;
29import org.springframework.batch.core.JobParameter;
30import org.springframework.batch.core.JobParameters;
31import org.springframework.batch.core.StepExecution;
32import org.springframework.batch.core.UnexpectedJobExecutionException;
33import org.springframework.batch.core.scope.StepScope;
34import org.springframework.batch.item.ExecutionContext;
35import org.springframework.batch.repeat.context.SynchronizedAttributeAccessor;
36import org.springframework.util.Assert;
37 
38/**
39 * A context object that can be used to interrogate the current
40 * {@link StepExecution} and some of its associated properties using expressions
41 * based on bean paths. Has public getters for the step execution and
42 * convenience methods for accessing commonly used properties like the
43 * {@link ExecutionContext} associated with the step or its enclosing job
44 * execution.
45 * 
46 * @author Dave Syer
47 * 
48 */
49public class StepContext extends SynchronizedAttributeAccessor {
50 
51        private StepExecution stepExecution;
52 
53        private Map<String, Set<Runnable>> callbacks = new HashMap<String, Set<Runnable>>();
54 
55        /**
56         * Create a new instance of {@link StepContext} for this
57         * {@link StepExecution}.
58         * 
59         * @param stepExecution a step execution
60         */
61        public StepContext(StepExecution stepExecution) {
62                super();
63                Assert.notNull(stepExecution, "A StepContext must have a non-null StepExecution");
64                this.stepExecution = stepExecution;
65        }
66 
67        /**
68         * Convenient accessor for current step name identifier. Usually this is the
69         * same as the bean name of the step that is executing (but might not be
70         * e.g. in a partition).
71         * 
72         * @return the step name identifier of the current {@link StepExecution}
73         */
74        public String getStepName() {
75                return stepExecution.getStepName();
76        }
77 
78        /**
79         * Convenient accessor for current job name identifier.
80         * 
81         * @return the job name identifier of the enclosing {@link JobInstance}
82         * associated with the current {@link StepExecution}
83         */
84        public String getJobName() {
85                Assert.state(stepExecution.getJobExecution() != null, "StepExecution does not have a JobExecution");
86                Assert.state(stepExecution.getJobExecution().getJobInstance() != null,
87                                "StepExecution does not have a JobInstance");
88                return stepExecution.getJobExecution().getJobInstance().getJobName();
89        }
90 
91        /**
92         * Convenient accessor for System properties to make it easy to access them
93         * from placeholder expressions.
94         * 
95         * @return the current System properties
96         */
97        public Properties getSystemProperties() {
98                return System.getProperties();
99        }
100 
101        /**
102         * @return a map containing the items from the step {@link ExecutionContext}
103         */
104        public Map<String, Object> getStepExecutionContext() {
105                Map<String, Object> result = new HashMap<String, Object>();
106                for (Entry<String, Object> entry : stepExecution.getExecutionContext().entrySet()) {
107                        result.put(entry.getKey(), entry.getValue());
108                }
109                return Collections.unmodifiableMap(result);
110        }
111 
112        /**
113         * @return a map containing the items from the job {@link ExecutionContext}
114         */
115        public Map<String, Object> getJobExecutionContext() {
116                Map<String, Object> result = new HashMap<String, Object>();
117                for (Entry<String, Object> entry : stepExecution.getJobExecution().getExecutionContext().entrySet()) {
118                        result.put(entry.getKey(), entry.getValue());
119                }
120                return Collections.unmodifiableMap(result);
121        }
122 
123        /**
124         * @return a map containing the items from the {@link JobParameters}
125         */
126        public Map<String, Object> getJobParameters() {
127                Map<String, Object> result = new HashMap<String, Object>();
128                for (Entry<String, JobParameter> entry : stepExecution.getJobParameters().getParameters().entrySet()) {
129                        result.put(entry.getKey(), entry.getValue().getValue());
130                }
131                return Collections.unmodifiableMap(result);
132        }
133 
134        /**
135         * Allow clients to register callbacks for clean up on close.
136         * 
137         * @param name the callback id (unique attribute key in this context)
138         * @param callback a callback to execute on close
139         */
140        public void registerDestructionCallback(String name, Runnable callback) {
141                synchronized (callbacks) {
142                        Set<Runnable> set = callbacks.get(name);
143                        if (set == null) {
144                                set = new HashSet<Runnable>();
145                                callbacks.put(name, set);
146                        }
147                        set.add(callback);
148                }
149        }
150 
151        private void unregisterDestructionCallbacks(String name) {
152                synchronized (callbacks) {
153                        callbacks.remove(name);
154                }
155        }
156 
157        /**
158         * Override base class behaviour to ensure destruction callbacks are
159         * unregistered as well as the default behaviour.
160         * 
161         * @see SynchronizedAttributeAccessor#removeAttribute(String)
162         */
163        @Override
164        public Object removeAttribute(String name) {
165                unregisterDestructionCallbacks(name);
166                return super.removeAttribute(name);
167        }
168 
169        /**
170         * Clean up the context at the end of a step execution. Must be called once
171         * at the end of a step execution to honour the destruction callback
172         * contract from the {@link StepScope}.
173         */
174        public void close() {
175 
176                List<Exception> errors = new ArrayList<Exception>();
177 
178                Map<String, Set<Runnable>> copy = Collections.unmodifiableMap(callbacks);
179 
180                for (Entry<String, Set<Runnable>> entry : copy.entrySet()) {
181                        Set<Runnable> set = entry.getValue();
182                        for (Runnable callback : set) {
183                                if (callback != null) {
184                                        /*
185                                         * The documentation of the interface says that these
186                                         * callbacks must not throw exceptions, but we don't trust
187                                         * them necessarily...
188                                         */
189                                        try {
190                                                callback.run();
191                                        }
192                                        catch (RuntimeException t) {
193                                                errors.add(t);
194                                        }
195                                }
196                        }
197                }
198 
199                if (errors.isEmpty()) {
200                        return;
201                }
202 
203                Exception error = errors.get(0);
204                if (error instanceof RuntimeException) {
205                        throw (RuntimeException) error;
206                }
207                else {
208                        throw new UnexpectedJobExecutionException("Could not close step context, rethrowing first of "
209                                        + errors.size() + " exceptions.", error);
210                }
211        }
212 
213        /**
214         * The current {@link StepExecution} that is active in this context.
215         * 
216         * @return the current {@link StepExecution}
217         */
218        public StepExecution getStepExecution() {
219                return stepExecution;
220        }
221 
222        /**
223         * @return unique identifier for this context based on the step execution
224         */
225        public String getId() {
226                Assert.state(stepExecution.getId() != null, "StepExecution has no id.  "
227                                + "It must be saved before it can be used in step scope.");
228                return "execution#" + stepExecution.getId();
229        }
230 
231        /**
232         * Extend the base class method to include the step execution itself as a
233         * key (i.e. two contexts are only equal if their step executions are the
234         * same).
235         * 
236         * @see SynchronizedAttributeAccessor#equals(Object)
237         */
238        @Override
239        public boolean equals(Object other) {
240                if (!(other instanceof StepContext))
241                        return false;
242                if (other == this)
243                        return true;
244                StepContext context = (StepContext) other;
245                if (context.stepExecution == stepExecution) {
246                        return true;
247                }
248                return stepExecution.equals(context.stepExecution);
249        }
250 
251        /**
252         * Overrides the default behaviour to provide a hash code based only on the
253         * step execution.
254         * 
255         * @see SynchronizedAttributeAccessor#hashCode()
256         */
257        @Override
258        public int hashCode() {
259                return stepExecution.hashCode();
260        }
261 
262        @Override
263        public String toString() {
264                return super.toString() + ", stepExecutionContext=" + getStepExecutionContext() + ", jobExecutionContext="
265                                + getJobExecutionContext() + ", jobParameters=" + getJobParameters();
266        }
267 
268}

[all classes][org.springframework.batch.core.scope.context]
EMMA 2.0.5312 (C) Vladimir Roubtsov