EMMA Coverage Report (generated Fri Aug 21 15:59:46 BST 2009)
[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)90%  (311/344)93%  (60.5/65)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class StepContext100% (1/1)94%  (15/16)90%  (311/344)93%  (60.5/65)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
equals (Object): boolean 100% (1/1)77%  (20/26)88%  (7/8)
close (): void 100% (1/1)80%  (60/75)88%  (14/16)
unregisterDestructionCallbacks (String): void 100% (1/1)82%  (14/17)94%  (2.8/3)
registerDestructionCallback (String, Runnable): void 100% (1/1)91%  (31/34)98%  (6.9/7)
getJobName (): String 100% (1/1)92%  (23/25)96%  (3.9/4)
StepContext (StepExecution): void 100% (1/1)100% (14/14)100% (5/5)
getId (): String 100% (1/1)100% (19/19)100% (2/2)
getJobExecutionContext (): Map 100% (1/1)100% (30/30)100% (4/4)
getJobParameters (): Map 100% (1/1)100% (32/32)100% (4/4)
getStepExecution (): StepExecution 100% (1/1)100% (3/3)100% (1/1)
getStepExecutionContext (): Map 100% (1/1)100% (29/29)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% (2/2)

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 * Simple implementation of {@link StepContext}.
40 * 
41 * @author Dave Syer
42 * 
43 */
44public class StepContext extends SynchronizedAttributeAccessor {
45 
46        private StepExecution stepExecution;
47 
48        private Map<String, Set<Runnable>> callbacks = new HashMap<String, Set<Runnable>>();
49 
50        /**
51         * Create a new instance of {@link StepContext} for this
52         * {@link StepExecution}.
53         * 
54         * @param stepExecution a step execution
55         */
56        public StepContext(StepExecution stepExecution) {
57                super();
58                Assert.notNull(stepExecution, "A StepContext must have a non-null StepExecution");
59                this.stepExecution = stepExecution;
60        }
61 
62        /**
63         * Convenient accessor for current step name identifier. Usually this is the
64         * same as the bean name of the step that is executing (but might not be
65         * e.g. in a partition).
66         * 
67         * @return the step name identifier of the current {@link StepExecution}
68         */
69        public String getStepName() {
70                return stepExecution.getStepName();
71        }
72 
73        /**
74         * Convenient accessor for current job name identifier.
75         * 
76         * @return the job name identifier of the enclosing {@link JobInstance}
77         * associated with the current {@link StepExecution}
78         */
79        public String getJobName() {
80                Assert.state(stepExecution.getJobExecution() != null, "StepExecution does not have a JobExecution");
81                Assert.state(stepExecution.getJobExecution().getJobInstance() != null,
82                                "StepExecution does not have a JobInstance");
83                return stepExecution.getJobExecution().getJobInstance().getJobName();
84        }
85 
86        /**
87         * Convenient accessor for System properties to make it easy to access them
88         * from placeholder expressions.
89         * 
90         * @return the current System properties
91         */
92        public Properties getSystemProperties() {
93                return System.getProperties();
94        }
95 
96        /**
97         * @return a map containing the items from the step {@link ExecutionContext}
98         */
99        public Map<String, Object> getStepExecutionContext() {
100                Map<String, Object> result = new HashMap<String, Object>();
101                for (Entry<String, Object> entry : stepExecution.getExecutionContext().entrySet()) {
102                        result.put(entry.getKey(), entry.getValue());
103                }
104                return Collections.unmodifiableMap(result);
105        }
106 
107        /**
108         * @return a map containing the items from the job {@link ExecutionContext}
109         */
110        public Map<String, Object> getJobExecutionContext() {
111                Map<String, Object> result = new HashMap<String, Object>();
112                for (Entry<String, Object> entry : stepExecution.getJobExecution().getExecutionContext().entrySet()) {
113                        result.put(entry.getKey(), entry.getValue());
114                }
115                return Collections.unmodifiableMap(result);
116        }
117 
118        /**
119         * @return a map containing the items from the {@link JobParameters}
120         */
121        public Map<String, Object> getJobParameters() {
122                Map<String, Object> result = new HashMap<String, Object>();
123                for (Entry<String, JobParameter> entry : stepExecution.getJobParameters().getParameters().entrySet()) {
124                        result.put(entry.getKey(), entry.getValue().getValue());
125                }
126                return Collections.unmodifiableMap(result);
127        }
128 
129        /**
130         * Allow clients to register callbacks for clean up on close.
131         * 
132         * @param name the callback id (unique attribute key in this context)
133         * @param callback a callback to execute on close
134         */
135        public void registerDestructionCallback(String name, Runnable callback) {
136                synchronized (callbacks) {
137                        Set<Runnable> set = callbacks.get(name);
138                        if (set == null) {
139                                set = new HashSet<Runnable>();
140                                callbacks.put(name, set);
141                        }
142                        set.add(callback);
143                }
144        }
145 
146        private void unregisterDestructionCallbacks(String name) {
147                synchronized (callbacks) {
148                        callbacks.remove(name);
149                }
150        }
151 
152        /**
153         * Override base class behaviour to ensure destruction callbacks are
154         * unregistered as well as the default behaviour.
155         * 
156         * @see SynchronizedAttributeAccessor#removeAttribute(String)
157         */
158        @Override
159        public Object removeAttribute(String name) {
160                unregisterDestructionCallbacks(name);
161                return super.removeAttribute(name);
162        }
163 
164        /**
165         * Clean up the context at the end of a step execution. Must be called once
166         * at the end of a step execution to honour the destruction callback
167         * contract from the {@link StepScope}.
168         */
169        public void close() {
170 
171                List<Exception> errors = new ArrayList<Exception>();
172 
173                Map<String, Set<Runnable>> copy = Collections.unmodifiableMap(callbacks);
174 
175                for (Entry<String, Set<Runnable>> entry : copy.entrySet()) {
176                        Set<Runnable> set = entry.getValue();
177                        for (Runnable callback : set) {
178                                if (callback != null) {
179                                        /*
180                                         * The documentation of the interface says that these
181                                         * callbacks must not throw exceptions, but we don't trust
182                                         * them necessarily...
183                                         */
184                                        try {
185                                                callback.run();
186                                        }
187                                        catch (RuntimeException t) {
188                                                errors.add(t);
189                                        }
190                                }
191                        }
192                }
193 
194                if (errors.isEmpty()) {
195                        return;
196                }
197 
198                Exception error = errors.get(0);
199                if (error instanceof RuntimeException) {
200                        throw (RuntimeException) error;
201                }
202                else {
203                        throw new UnexpectedJobExecutionException("Could not close step context, rethrowing first of "
204                                        + errors.size() + " exceptions.", error);
205                }
206        }
207 
208        public StepExecution getStepExecution() {
209                return stepExecution;
210        }
211 
212        /**
213         * @return unique identifier for this context based on the step execution
214         */
215        public String getId() {
216                Assert.state(stepExecution.getId() != null, "StepExecution has no id.  "
217                                + "It must be saved before it can be used in step scope.");
218                return "execution#" + stepExecution.getId();
219        }
220 
221        /**
222         * Extend the base class method to include the step execution itself as a
223         * key (i.e. two contexts are only equal if their step executions are the
224         * same).
225         * 
226         * @see SynchronizedAttributeAccessor#equals(Object)
227         */
228        @Override
229        public boolean equals(Object other) {
230                if (!(other instanceof StepContext))
231                        return false;
232                if (other == this)
233                        return true;
234                StepContext context = (StepContext) other;
235                if (context.stepExecution == stepExecution) {
236                        return true;
237                }
238                return stepExecution.equals(context.stepExecution);
239        }
240 
241        /**
242         * Overrides the default behaviour to provide a hash code based only on the
243         * step execution.
244         * 
245         * @see SynchronizedAttributeAccessor#hashCode()
246         */
247        @Override
248        public int hashCode() {
249                return stepExecution.hashCode();
250        }
251 
252        @Override
253        public String toString() {
254                return super.toString() + ", stepExecutionContext=" + getStepExecutionContext() + ", jobExecutionContext="
255                                + getJobExecutionContext() + ", jobParameters=" + getJobParameters();
256        }
257 
258}

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