EMMA Coverage Report (generated Fri Aug 21 15:59:46 BST 2009)
[all classes][org.springframework.batch.core.listener]

COVERAGE SUMMARY FOR SOURCE FILE [AbstractListenerFactoryBean.java]

nameclass, %method, %block, %line, %
AbstractListenerFactoryBean.java100% (2/2)100% (10/10)99%  (263/265)98%  (54/55)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractListenerFactoryBean100% (1/1)100% (8/8)99%  (252/254)98%  (50/51)
isListener (Object, Class, ListenerMetaData []): boolean 100% (1/1)96%  (48/50)90%  (9/10)
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% (176/176)100% (30/30)
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)
     
class AbstractListenerFactoryBean$NullIgnoringSet100% (1/1)100% (2/2)100% (11/11)100% (4/4)
AbstractListenerFactoryBean$NullIgnoringSet (): void 100% (1/1)100% (3/3)100% (1/1)
add (Object): boolean 100% (1/1)100% (8/8)100% (3/3)

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

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