EMMA Coverage Report (generated Thu May 22 12:08:10 CDT 2014)
[all classes][org.springframework.batch.core.scope]

COVERAGE SUMMARY FOR SOURCE FILE [StepScope.java]

nameclass, %method, %block, %line, %
StepScope.java100% (3/3)100% (19/19)99%  (341/346)99%  (81.4/82)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class StepScope100% (1/1)100% (15/15)98%  (257/262)99%  (58.4/59)
get (String, ObjectFactory): Object 100% (1/1)91%  (48/53)94%  (10.4/11)
StepScope (): void 100% (1/1)100% (25/25)100% (8/8)
access$000 (String, BeanDefinition, BeanDefinitionRegistry, boolean): BeanDef... 100% (1/1)100% (6/6)100% (1/1)
createScopedProxy (String, BeanDefinition, BeanDefinitionRegistry, boolean): ... 100% (1/1)100% (16/16)100% (3/3)
getContext (): StepContext 100% (1/1)100% (11/11)100% (4/4)
getConversationId (): String 100% (1/1)100% (6/6)100% (2/2)
getOrder (): int 100% (1/1)100% (3/3)100% (1/1)
postProcessBeanFactory (ConfigurableListableBeanFactory): void 100% (1/1)100% (69/69)100% (13/13)
registerDestructionCallback (String, Runnable): void 100% (1/1)100% (24/24)100% (4/4)
remove (String): Object 100% (1/1)100% (23/23)100% (3/3)
resolveContextualObject (String): Object 100% (1/1)100% (10/10)100% (2/2)
setAutoProxy (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setName (String): void 100% (1/1)100% (4/4)100% (2/2)
setOrder (int): void 100% (1/1)100% (4/4)100% (2/2)
setProxyTargetClass (boolean): void 100% (1/1)100% (4/4)100% (2/2)
     
class StepScope$Scopifier100% (1/1)100% (2/2)100% (79/79)100% (22/22)
StepScope$Scopifier (BeanDefinitionRegistry, String, boolean, boolean): void 100% (1/1)100% (18/18)100% (6/6)
resolveValue (Object): Object 100% (1/1)100% (61/61)100% (16/16)
     
class StepScope$Scopifier$1100% (1/1)100% (2/2)100% (5/5)100% (2/2)
StepScope$Scopifier$1 (): void 100% (1/1)100% (3/3)100% (1/1)
resolveStringValue (String): String 100% (1/1)100% (2/2)100% (1/1)

1/*
2 * Copyright 2006-2013 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;
17 
18import org.apache.commons.logging.Log;
19import org.apache.commons.logging.LogFactory;
20import org.springframework.aop.scope.ScopedProxyUtils;
21import org.springframework.batch.core.scope.context.StepContext;
22import org.springframework.batch.core.scope.context.StepSynchronizationManager;
23import org.springframework.beans.BeanWrapper;
24import org.springframework.beans.BeanWrapperImpl;
25import org.springframework.beans.BeansException;
26import org.springframework.beans.factory.ObjectFactory;
27import org.springframework.beans.factory.config.BeanDefinition;
28import org.springframework.beans.factory.config.BeanDefinitionHolder;
29import org.springframework.beans.factory.config.BeanDefinitionVisitor;
30import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
31import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
32import org.springframework.beans.factory.config.Scope;
33import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
34import org.springframework.beans.factory.support.BeanDefinitionRegistry;
35import org.springframework.core.Ordered;
36import org.springframework.util.Assert;
37import org.springframework.util.StringValueResolver;
38 
39/**
40 * Scope for step context. Objects in this scope use the Spring container as an
41 * object factory, so there is only one instance of such a bean per executing
42 * step. All objects in this scope are <aop:scoped-proxy/> (no need to
43 * decorate the bean definitions).<br/>
44 * <br/>
45 *
46 * In addition, support is provided for late binding of references accessible
47 * from the {@link StepContext} using #{..} placeholders. Using this feature,
48 * bean properties can be pulled from the step or job execution context and the
49 * job parameters. E.g.
50 *
51 * <pre>
52 * &lt;bean id=&quot;...&quot; class=&quot;...&quot; scope=&quot;step&quot;&gt;
53 *         &lt;property name=&quot;parent&quot; ref=&quot;#{stepExecutionContext[helper]}&quot; /&gt;
54 * &lt;/bean&gt;
55 *
56 * &lt;bean id=&quot;...&quot; class=&quot;...&quot; scope=&quot;step&quot;&gt;
57 *         &lt;property name=&quot;name&quot; value=&quot;#{stepExecutionContext['input.name']}&quot; /&gt;
58 * &lt;/bean&gt;
59 *
60 * &lt;bean id=&quot;...&quot; class=&quot;...&quot; scope=&quot;step&quot;&gt;
61 *         &lt;property name=&quot;name&quot; value=&quot;#{jobParameters[input]}&quot; /&gt;
62 * &lt;/bean&gt;
63 *
64 * &lt;bean id=&quot;...&quot; class=&quot;...&quot; scope=&quot;step&quot;&gt;
65 *         &lt;property name=&quot;name&quot; value=&quot;#{jobExecutionContext['input.stem']}.txt&quot; /&gt;
66 * &lt;/bean&gt;
67 * </pre>
68 *
69 * The {@link StepContext} is referenced using standard bean property paths (as
70 * per {@link BeanWrapper}). The examples above all show the use of the Map
71 * accessors provided as a convenience for step and job attributes.
72 *
73 * @author Dave Syer
74 * @author Michael Minella
75 * @since 2.0
76 */
77public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered {
78 
79        private Log logger = LogFactory.getLog(getClass());
80 
81        private int order = Ordered.LOWEST_PRECEDENCE;
82 
83        private boolean autoProxy = true;
84 
85        private final Object mutex = new Object();
86 
87        /**
88         * @param order the order value to set priority of callback execution for
89         * the {@link BeanFactoryPostProcessor} part of this scope bean.
90         */
91        public void setOrder(int order) {
92                this.order = order;
93        }
94 
95        @Override
96        public int getOrder() {
97                return order;
98        }
99 
100        /**
101         * Context key for clients to use for conversation identifier.
102         */
103        public static final String ID_KEY = "STEP_IDENTIFIER";
104 
105        private String name = "step";
106 
107        private boolean proxyTargetClass = false;
108 
109        /**
110         * Flag to indicate that proxies should use dynamic subclassing. This allows
111         * classes with no interface to be proxied. Defaults to false.
112         *
113         * @param proxyTargetClass set to true to have proxies created using dynamic
114         * subclasses
115         */
116        public void setProxyTargetClass(boolean proxyTargetClass) {
117                this.proxyTargetClass = proxyTargetClass;
118        }
119 
120        /**
121         * Flag to indicate that bean definitions need not be auto proxied. This gives control back to the declarer of the
122         * bean definition (e.g. in an &#64;Configuration class).
123         *
124         * @param autoProxy the flag value to set (default true)
125         */
126        public void setAutoProxy(boolean autoProxy) {
127                this.autoProxy = autoProxy;
128        }
129 
130        /**
131         * This will be used to resolve expressions in step-scoped beans.
132         */
133        @Override
134        public Object resolveContextualObject(String key) {
135                StepContext context = getContext();
136                // TODO: support for attributes as well maybe (setters not exposed yet
137                // so not urgent).
138                return new BeanWrapperImpl(context).getPropertyValue(key);
139        }
140 
141        /**
142         * @see Scope#get(String, ObjectFactory)
143         */
144        @SuppressWarnings("rawtypes")
145        @Override
146        public Object get(String name, ObjectFactory objectFactory) {
147                StepContext context = getContext();
148                Object scopedObject = context.getAttribute(name);
149 
150                if (scopedObject == null) {
151 
152                        synchronized (mutex) {
153                                scopedObject = context.getAttribute(name);
154                                if (scopedObject == null) {
155 
156                                        logger.debug(String.format("Creating object in scope=%s, name=%s", this.name, name));
157 
158                                        scopedObject = objectFactory.getObject();
159                                        context.setAttribute(name, scopedObject);
160 
161                                }
162 
163                        }
164 
165                }
166                return scopedObject;
167        }
168 
169        /**
170         * @see Scope#getConversationId()
171         */
172        @Override
173        public String getConversationId() {
174                StepContext context = getContext();
175                return context.getId();
176        }
177 
178        /**
179         * @see Scope#registerDestructionCallback(String, Runnable)
180         */
181        @Override
182        public void registerDestructionCallback(String name, Runnable callback) {
183                StepContext context = getContext();
184                logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.name, name));
185                context.registerDestructionCallback(name, callback);
186        }
187 
188        /**
189         * @see Scope#remove(String)
190         */
191        @Override
192        public Object remove(String name) {
193                StepContext context = getContext();
194                logger.debug(String.format("Removing from scope=%s, name=%s", this.name, name));
195                return context.removeAttribute(name);
196        }
197 
198        /**
199         * Get an attribute accessor in the form of a {@link StepContext} that can
200         * be used to store scoped bean instances.
201         *
202         * @return the current step context which we can use as a scope storage
203         * medium
204         */
205        private StepContext getContext() {
206                StepContext context = StepSynchronizationManager.getContext();
207                if (context == null) {
208                        throw new IllegalStateException("No context holder available for step scope");
209                }
210                return context;
211        }
212 
213        /**
214         * Register this scope with the enclosing BeanFactory.
215         *
216         * @see BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory)
217         *
218         * @param beanFactory the BeanFactory to register with
219         * @throws BeansException if there is a problem.
220         */
221        @Override
222        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
223 
224                beanFactory.registerScope(name, this);
225 
226                if(!autoProxy) {
227                        return;
228                }
229 
230                Assert.state(beanFactory instanceof BeanDefinitionRegistry,
231                                "BeanFactory was not a BeanDefinitionRegistry, so StepScope cannot be used.");
232                BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
233 
234                for (String beanName : beanFactory.getBeanDefinitionNames()) {
235                        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
236                        // Replace this or any of its inner beans with scoped proxy if it
237                        // has this scope
238                        boolean scoped = name.equals(definition.getScope());
239                        Scopifier scopifier = new Scopifier(registry, name, proxyTargetClass, scoped);
240                        scopifier.visitBeanDefinition(definition);
241 
242                        if (scoped && !definition.isAbstract()) {
243                                createScopedProxy(beanName, definition, registry, proxyTargetClass);
244                        }
245                }
246 
247        }
248 
249        /**
250         * Public setter for the name property. This can then be used as a bean
251         * definition attribute, e.g. scope="step". Defaults to "step".
252         *
253         * @param name the name to set for this scope.
254         */
255        public void setName(String name) {
256                this.name = name;
257        }
258 
259        /**
260         * Wrap a target bean definition in a proxy that defers initialization until
261         * after the {@link StepContext} is available. Amounts to adding
262         * &lt;aop-auto-proxy/&gt; to a step scoped bean.
263         *
264         * @param beanName the bean name to replace
265         * @param definition the bean definition to replace
266         * @param registry the enclosing {@link BeanDefinitionRegistry}
267         * @param proxyTargetClass true if we need to force use of dynamic
268         * subclasses
269         * @return a {@link BeanDefinitionHolder} for the new representation of the
270         * target. Caller should register it if needed to be visible at top level in
271         * bean factory.
272         */
273        private static BeanDefinitionHolder createScopedProxy(String beanName, BeanDefinition definition,
274                        BeanDefinitionRegistry registry, boolean proxyTargetClass) {
275 
276                BeanDefinitionHolder proxyHolder;
277 
278                proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry,
279                                proxyTargetClass);
280 
281                registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
282 
283                return proxyHolder;
284 
285        }
286 
287        /**
288         * Helper class to scan a bean definition hierarchy and force the use of
289         * auto-proxy for step scoped beans.
290         *
291         * @author Dave Syer
292         *
293         */
294        private static class Scopifier extends BeanDefinitionVisitor {
295 
296                private final boolean proxyTargetClass;
297 
298                private final BeanDefinitionRegistry registry;
299 
300                private final String scope;
301 
302                private final boolean scoped;
303 
304                public Scopifier(BeanDefinitionRegistry registry, String scope, boolean proxyTargetClass, boolean scoped) {
305                        super(new StringValueResolver() {
306                                @Override
307                                public String resolveStringValue(String value) {
308                                        return value;
309                                }
310                        });
311                        this.registry = registry;
312                        this.proxyTargetClass = proxyTargetClass;
313                        this.scope = scope;
314                        this.scoped = scoped;
315                }
316 
317                @Override
318                protected Object resolveValue(Object value) {
319 
320                        BeanDefinition definition = null;
321                        String beanName = null;
322                        if (value instanceof BeanDefinition) {
323                                definition = (BeanDefinition) value;
324                                beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry);
325                        }
326                        else if (value instanceof BeanDefinitionHolder) {
327                                BeanDefinitionHolder holder = (BeanDefinitionHolder) value;
328                                definition = holder.getBeanDefinition();
329                                beanName = holder.getBeanName();
330                        }
331 
332                        if (definition != null) {
333                                boolean nestedScoped = scope.equals(definition.getScope());
334                                boolean scopeChangeRequiresProxy = !scoped && nestedScoped;
335                                if (scopeChangeRequiresProxy) {
336                                        // Exit here so that nested inner bean definitions are not
337                                        // analysed
338                                        return createScopedProxy(beanName, definition, registry, proxyTargetClass);
339                                }
340                        }
341 
342                        // Nested inner bean definitions are recursively analysed here
343                        value = super.resolveValue(value);
344                        return value;
345 
346                }
347 
348        }
349}

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