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

COVERAGE SUMMARY FOR SOURCE FILE [AbstractListenerFactoryBean.java]

nameclass, %method, %block, %line, %
AbstractListenerFactoryBean.java100% (1/1)100% (9/9)96%  (315/329)97%  (71/73)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractListenerFactoryBean100% (1/1)100% (9/9)96%  (315/329)97%  (71/73)
isListener (Object, Class, ListenerMetaData []): boolean 100% (1/1)81%  (61/75)86%  (12/14)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
AbstractListenerFactoryBean (): void 100% (1/1)100% (3/3)100% (1/1)
afterPropertiesSet (): void 100% (1/1)100% (5/5)100% (2/2)
getMethodInvokerByName (String, Object, Class []): MethodInvoker 100% (1/1)100% (10/10)100% (3/3)
getObject (): Object 100% (1/1)100% (222/222)100% (47/47)
isSingleton (): boolean 100% (1/1)100% (2/2)100% (1/1)
setDelegate (Object): void 100% (1/1)100% (4/4)100% (2/2)
setMetaDataMap (Map): void 100% (1/1)100% (4/4)100% (2/2)

1/*
2 * Copyright 2002-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.listener;
17 
18import static org.springframework.batch.support.MethodInvokerUtils.getMethodInvokerByAnnotation;
19import static org.springframework.batch.support.MethodInvokerUtils.getMethodInvokerForInterface;
20 
21import java.util.HashMap;
22import java.util.HashSet;
23import java.util.Map;
24import java.util.Map.Entry;
25import java.util.Set;
26 
27import org.apache.commons.logging.Log;
28import org.apache.commons.logging.LogFactory;
29import org.springframework.aop.TargetSource;
30import org.springframework.aop.framework.Advised;
31import org.springframework.aop.framework.ProxyFactory;
32import org.springframework.aop.support.DefaultPointcutAdvisor;
33import org.springframework.batch.support.MethodInvoker;
34import org.springframework.batch.support.MethodInvokerUtils;
35import org.springframework.beans.factory.FactoryBean;
36import org.springframework.beans.factory.InitializingBean;
37import org.springframework.core.Ordered;
38import org.springframework.util.Assert;
39 
40/**
41 * {@link FactoryBean} implementation that builds a listener based on the
42 * various lifecycle methods or annotations that are provided. There are three
43 * possible ways of having a method called as part of a listener lifecycle:
44 *
45 * <ul>
46 * <li>Interface implementation: By implementing any of the subclasses of a
47 * listener interface, methods on said interface will be called
48 * <li>Annotations: Annotating a method will result in registration.
49 * <li>String name of the method to be called, which is tied to a
50 * {@link ListenerMetaData} value in the metaDataMap.
51 * </ul>
52 *
53 * It should be noted that methods obtained by name or annotation that don't
54 * match the listener method signatures to which they belong will cause errors.
55 * However, it is acceptable to have no parameters at all. If the same method is
56 * marked in more than one way. (i.e. the method name is given and it is
57 * annotated) the method will only be called once. However, if the same class
58 * has multiple methods tied to a particular listener, each method will be
59 * called. Also note that the same annotations cannot be applied to two separate
60 * methods in a single class.
61 *
62 * @author Lucas Ward
63 * @author Dan Garrette
64 * @since 2.0
65 * @see ListenerMetaData
66 */
67@SuppressWarnings("rawtypes")
68public abstract class AbstractListenerFactoryBean implements FactoryBean, InitializingBean {
69 
70        private static final Log logger = LogFactory.getLog(AbstractListenerFactoryBean.class);
71 
72        private Object delegate;
73 
74        private Map<String, String> metaDataMap;
75 
76        @Override
77        public Object getObject() {
78 
79                if (metaDataMap == null) {
80                        metaDataMap = new HashMap<String, String>();
81                }
82                // Because all annotations and interfaces should be checked for, make
83                // sure that each meta data
84                // entry is represented.
85                for (ListenerMetaData metaData : this.getMetaDataValues()) {
86                        if (!metaDataMap.containsKey(metaData.getPropertyName())) {
87                                // put null so that the annotation and interface is checked
88                                metaDataMap.put(metaData.getPropertyName(), null);
89                        }
90                }
91 
92                Set<Class<?>> listenerInterfaces = new HashSet<Class<?>>();
93 
94                // For every entry in the map, try and find a method by interface, name,
95                // or annotation. If the same
96                Map<String, Set<MethodInvoker>> invokerMap = new HashMap<String, Set<MethodInvoker>>();
97                boolean synthetic = false;
98                for (Entry<String, String> entry : metaDataMap.entrySet()) {
99 
100                        final ListenerMetaData metaData = this.getMetaDataFromPropertyName(entry.getKey());
101                        Set<MethodInvoker> invokers = new HashSet<MethodInvoker>();
102 
103                        MethodInvoker invoker;
104 
105                        invoker = getMethodInvokerForInterface(metaData.getListenerInterface(), metaData.getMethodName(), delegate,
106                                        metaData.getParamTypes());
107                        if (invoker != null) {
108                                invokers.add(invoker);
109                        }
110 
111                        invoker = getMethodInvokerByName(entry.getValue(), delegate, metaData.getParamTypes());
112                        if (invoker != null) {
113                                invokers.add(invoker);
114                                synthetic = true;
115                        }
116 
117                        invoker = getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate, metaData.getParamTypes());
118                        if (invoker != null) {
119                                invokers.add(invoker);
120                                synthetic = true;
121                        }
122 
123                        if (!invokers.isEmpty()) {
124                                invokerMap.put(metaData.getMethodName(), invokers);
125                                listenerInterfaces.add(metaData.getListenerInterface());
126                        }
127 
128                }
129 
130                if (listenerInterfaces.isEmpty()) {
131                        listenerInterfaces.add(this.getDefaultListenerClass());
132                }
133 
134                if (!synthetic) {
135                        int count = 0;
136                        for (Class<?> listenerInterface : listenerInterfaces) {
137                                if (listenerInterface.isInstance(delegate)) {
138                                        count++;
139                                }
140                        }
141                        // All listeners can be supplied by the delegate itself
142                        if (count == listenerInterfaces.size()) {
143                                return delegate;
144                        }
145                }
146 
147                boolean ordered = false;
148                if (delegate instanceof Ordered) {
149                        ordered = true;
150                        listenerInterfaces.add(Ordered.class);
151                }
152 
153                // create a proxy listener for only the interfaces that have methods to
154                // be called
155                ProxyFactory proxyFactory = new ProxyFactory();
156                if (delegate instanceof Advised) {
157                        proxyFactory.setTargetSource(((Advised) delegate).getTargetSource());
158                }
159                else {
160                        proxyFactory.setTarget(delegate);
161                }
162                proxyFactory.setInterfaces(listenerInterfaces.toArray(new Class[0]));
163                proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new MethodInvokerMethodInterceptor(invokerMap, ordered)));
164                return proxyFactory.getProxy();
165 
166        }
167 
168        protected abstract ListenerMetaData getMetaDataFromPropertyName(String propertyName);
169 
170        protected abstract ListenerMetaData[] getMetaDataValues();
171 
172        protected abstract Class<?> getDefaultListenerClass();
173 
174        protected MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class<?>... params) {
175                if (methodName != null) {
176                        return MethodInvokerUtils.getMethodInvokerByName(candidate, methodName, false, params);
177                }
178                else {
179                        return null;
180                }
181        }
182 
183        @Override
184        public boolean isSingleton() {
185                return true;
186        }
187 
188        public void setDelegate(Object delegate) {
189                this.delegate = delegate;
190        }
191 
192        public void setMetaDataMap(Map<String, String> metaDataMap) {
193                this.metaDataMap = metaDataMap;
194        }
195 
196        @Override
197        public void afterPropertiesSet() throws Exception {
198                Assert.notNull(delegate, "Delegate must not be null");
199        }
200 
201        /**
202         * Convenience method to check whether the given object is or can be made
203         * into a listener.
204         *
205         * @param target the object to check
206         * @return true if the delegate is an instance of any of the listener
207         * interface, or contains the marker annotations
208         */
209        public static boolean isListener(Object target, Class<?> listenerType, ListenerMetaData[] metaDataValues) {
210                if (target == null) {
211                        return false;
212                }
213                if (listenerType.isInstance(target)) {
214                        return true;
215                }
216                if (target instanceof Advised) {
217                        TargetSource targetSource = ((Advised) target).getTargetSource();
218                        if (targetSource != null && targetSource.getTargetClass() != null
219                                        && listenerType.isAssignableFrom(targetSource.getTargetClass())) {
220                                return true;
221                        }
222 
223                        if(targetSource != null && targetSource.getTargetClass() != null && targetSource.getTargetClass().isInterface()) {
224                                logger.warn(String.format("%s is an interface.  The implementing class will not be queried for annotation based listener configurations.  If using @StepScope on a @Bean method, be sure to return the implementing class so listner annotations can be used.", targetSource.getTargetClass().getName()));
225                        }
226                }
227                for (ListenerMetaData metaData : metaDataValues) {
228                        if (MethodInvokerUtils.getMethodInvokerByAnnotation(metaData.getAnnotation(), target) != null) {
229                                return true;
230                        }
231                }
232                return false;
233        }
234}

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