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

COVERAGE SUMMARY FOR SOURCE FILE [StepScope.java]

nameclass, %method, %block, %line, %
StepScope.java83%  (5/6)96%  (24/25)94%  (458/489)96%  (104/108)

COVERAGE BREAKDOWN BY CLASS AND METHOD

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

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