001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.spring.test;
018
019import java.io.IOException;
020import java.io.Serializable;
021import java.lang.annotation.Annotation;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.Locale;
026import java.util.Map;
027import java.util.Map.Entry;
028
029import org.springframework.beans.BeansException;
030import org.springframework.beans.factory.BeanFactory;
031import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
032import org.springframework.beans.factory.FactoryBean;
033import org.springframework.beans.factory.NoSuchBeanDefinitionException;
034import org.springframework.beans.factory.ObjectProvider;
035import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
036import org.springframework.context.ApplicationContext;
037import org.springframework.context.ApplicationEvent;
038import org.springframework.context.MessageSourceResolvable;
039import org.springframework.context.NoSuchMessageException;
040import org.springframework.core.ResolvableType;
041import org.springframework.core.env.Environment;
042import org.springframework.core.io.Resource;
043
044/**
045 * Mock application context object. This mock context allows easy creation of unit tests by allowing
046 * the user to put bean instances into the context.
047 * 
048 * Only {@link #getBean(String)}, {@link #getBean(String, Class)}, and
049 * {@link #getBeansOfType(Class)
050 * } are implemented so far. Any other method throws
051 * {@link UnsupportedOperationException}.
052 * 
053 * @author Igor Vaynberg (ivaynberg)
054 * 
055 */
056public class ApplicationContextMock implements ApplicationContext, Serializable
057{
058        private static final long serialVersionUID = 1L;
059
060        private final Map<String, Object> beans = new HashMap<>();
061
062        /**
063         * puts bean with the given name into the context
064         * 
065         * @param name
066         * @param bean
067         */
068        public void putBean(final String name, final Object bean)
069        {
070                if (beans.containsKey(name))
071                {
072                        throw new IllegalArgumentException("a bean with name [" + name +
073                                "] has already been added to the context");
074                }
075                beans.put(name, bean);
076        }
077
078        /**
079         * puts bean with into the context. bean object's class name will be used as the bean name.
080         * 
081         * @param bean
082         */
083        public void putBean(final Object bean)
084        {
085                putBean(bean.getClass().getName(), bean);
086        }
087
088        @Override
089        public Object getBean(final String name) throws BeansException
090        {
091                Object bean = beans.get(name);
092                if (bean == null)
093                {
094                        throw new NoSuchBeanDefinitionException(name);
095                }
096                return bean;
097        }
098
099        @Override
100        public Object getBean(final String name, final Object... args) throws BeansException
101        {
102                return getBean(name);
103        }
104
105        /**
106         * @see org.springframework.beans.factory.BeanFactory#getBean(java.lang.String, java.lang.Class)
107         */
108        @Override
109        @SuppressWarnings({ "unchecked" })
110        public <T> T getBean(String name, Class<T> requiredType) throws BeansException
111        {
112                Object bean = getBean(name);
113                if (!(requiredType.isAssignableFrom(bean.getClass())))
114                {
115                        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
116                }
117                return (T)bean;
118        }
119
120        /**
121         * @see org.springframework.beans.factory.ListableBeanFactory#getBeansOfType(java.lang.Class)
122         */
123        @Override
124        @SuppressWarnings({ "unchecked" })
125        public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException
126        {
127                final Map<String, T> found = new HashMap<>();
128
129                for (Entry<String, Object> entry : beans.entrySet())
130                {
131                        if (type.isAssignableFrom(entry.getValue().getClass()))
132                        {
133                                found.put(entry.getKey(), (T)entry.getValue());
134                        }
135                }
136
137                return found;
138        }
139
140        @Override
141        public <T> T getBean(Class<T> requiredType) throws BeansException
142        {
143                Iterator<T> beans = getBeansOfType(requiredType).values().iterator();
144
145                if (beans.hasNext() == false)
146                {
147                        throw new NoSuchBeanDefinitionException("bean of required type " + requiredType +
148                                " not found");
149                }
150                final T bean = beans.next();
151
152                if (beans.hasNext() != false)
153                {
154                        throw new NoSuchBeanDefinitionException("more than one bean of required type " +
155                                requiredType + " found");
156                }
157                return bean;
158        }
159
160        @Override
161        public <T> T getBean(Class<T> requiredType, Object... objects) throws BeansException
162        {
163                return getBean(requiredType);
164        }
165
166        @Override
167        public <T> ObjectProvider<T> getBeanProvider(Class<T> aClass)
168        {
169                return null;
170        }
171
172        @Override
173        public <T> ObjectProvider<T> getBeanProvider(ResolvableType resolvableType)
174        {
175                return null;
176        }
177
178        @Override
179        public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
180                throws BeansException
181        {
182                final Map<String, Object> found = new HashMap<>();
183
184                for (Entry<String, Object> entry : beans.entrySet())
185                {
186                        if (entry.getValue().getClass().isAnnotationPresent(annotationType))
187                        {
188                                found.put(entry.getKey(), entry.getValue());
189                        }
190                }
191                return found;
192        }
193
194        @Override
195        public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
196        {
197                return findAnnotationOnClass(getBean(beanName).getClass(), annotationType);
198        }
199
200        @Override
201        public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException {
202                return findAnnotationOnClass(getBean(beanName).getClass(), annotationType);
203        }
204
205        private <A extends Annotation> A findAnnotationOnClass(Class<?> cls, Class<A> annotationType)
206        {
207                // lookup annotation type on class
208                A annotation = cls.getAnnotation(annotationType);
209
210                // lookup annotation type on superclass
211                if (annotation == null && cls.getSuperclass() != null)
212                {
213                        annotation = findAnnotationOnClass(cls.getSuperclass(), annotationType);
214                }
215
216                // lookup annotation type on interfaces
217                if (annotation == null)
218                {
219                        for (Class<?> intfClass : cls.getInterfaces())
220                        {
221                                annotation = findAnnotationOnClass(intfClass, annotationType);
222
223                                if (annotation != null)
224                                {
225                                        break;
226                                }
227                        }
228                }
229
230                return annotation;
231        }
232
233        @Override
234        public ApplicationContext getParent()
235        {
236                throw new UnsupportedOperationException();
237        }
238
239        @Override
240        public String getDisplayName()
241        {
242                throw new UnsupportedOperationException();
243        }
244
245        @Override
246        public long getStartupDate()
247        {
248                throw new UnsupportedOperationException();
249        }
250
251        @Override
252        public void publishEvent(final ApplicationEvent event)
253        {
254                throw new UnsupportedOperationException();
255        }
256
257        @Override
258        public void publishEvent(Object o)
259        {
260                throw new UnsupportedOperationException();
261        }
262
263        @Override
264        public boolean containsBeanDefinition(final String beanName)
265        {
266                return containsBean(beanName);
267        }
268
269        @Override
270        public int getBeanDefinitionCount()
271        {
272                return beans.size();
273        }
274
275        @Override
276        public String[] getBeanDefinitionNames()
277        {
278                return beans.keySet().toArray(new String[0]);
279        }
280
281        @Override
282        public <T> ObjectProvider<T> getBeanProvider(final Class<T> aClass, final boolean b) {
283                return null;
284        }
285
286        @Override
287        public <T> ObjectProvider<T> getBeanProvider(final ResolvableType resolvableType, final boolean b) {
288                return null;
289        }
290
291        @Override
292        public String[] getBeanNamesForType(ResolvableType resolvableType)
293        {
294                return new String[0];
295        }
296
297        @Override
298        public String[] getBeanNamesForType(ResolvableType resolvableType, boolean includeNonSingletons, boolean allowEagerInit)
299        {
300                return new String[0];
301        }
302
303        @Override
304        @SuppressWarnings({ "unchecked" })
305        public String[] getBeanNamesForType(final Class type)
306        {
307                ArrayList<String> names = new ArrayList<>();
308                for (Entry<String, Object> entry : beans.entrySet())
309                {
310                        Object bean = entry.getValue();
311
312                        if (type.isAssignableFrom(bean.getClass()))
313                        {
314                                names.add(entry.getKey());
315                        }
316                }
317                return names.toArray(new String[names.size()]);
318        }
319
320        @Override
321        @SuppressWarnings({ "unchecked" })
322        public String[] getBeanNamesForType(Class type, boolean includeNonSingletons,
323                boolean allowEagerInit)
324        {
325                throw new UnsupportedOperationException();
326        }
327
328        @Override
329        public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons,
330                boolean allowEagerInit) throws BeansException
331        {
332                throw new UnsupportedOperationException();
333        }
334
335        @Override
336        public String[] getBeanNamesForAnnotation(Class<? extends Annotation> aClass)
337        {
338                throw new UnsupportedOperationException();
339        }
340
341        @Override
342        public boolean containsBean(final String name)
343        {
344                return beans.containsKey(name);
345        }
346
347        @Override
348        public boolean isSingleton(final String name) throws NoSuchBeanDefinitionException
349        {
350                return true;
351        }
352
353        @Override
354        public Class<?> getType(final String name) throws NoSuchBeanDefinitionException
355        {
356                return getType(name, true);
357        }
358
359        @Override
360        public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException
361        {
362                Object bean = beans.get(name);
363                if (bean == null)
364                {
365                        throw new NoSuchBeanDefinitionException("No bean with name '" + name + "'");
366                }
367
368                if (bean instanceof FactoryBean)
369                {
370                        return ((FactoryBean) bean).getObjectType();
371                }
372                
373                return bean.getClass();
374        }
375
376        @Override
377        public String[] getAliases(final String name) throws NoSuchBeanDefinitionException
378        {
379                throw new UnsupportedOperationException();
380        }
381
382        /**
383         * @see org.springframework.beans.factory.HierarchicalBeanFactory#getParentBeanFactory()
384         */
385        @Override
386        public BeanFactory getParentBeanFactory()
387        {
388                return null;
389        }
390
391        @Override
392        public String getMessage(final String code, final Object[] args, final String defaultMessage,
393                final Locale locale)
394        {
395                throw new UnsupportedOperationException();
396        }
397
398        @Override
399        public String getMessage(final String code, final Object[] args, final Locale locale)
400                throws NoSuchMessageException
401        {
402                throw new UnsupportedOperationException();
403        }
404
405        @Override
406        public String getMessage(final MessageSourceResolvable resolvable, final Locale locale)
407                throws NoSuchMessageException
408        {
409                throw new UnsupportedOperationException();
410        }
411
412        @Override
413        public Resource[] getResources(final String locationPattern) throws IOException
414        {
415                throw new UnsupportedOperationException();
416        }
417
418        @Override
419        public Resource getResource(final String location)
420        {
421                throw new UnsupportedOperationException();
422        }
423
424        @Override
425        public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException
426        {
427                throw new UnsupportedOperationException();
428        }
429
430        @Override
431        public boolean containsLocalBean(final String arg0)
432        {
433                throw new UnsupportedOperationException();
434        }
435
436        @Override
437        public ClassLoader getClassLoader()
438        {
439                throw new UnsupportedOperationException();
440        }
441
442        @Override
443        public String getId()
444        {
445                return null;
446        }
447
448        @Override
449        public String getApplicationName()
450        {
451                return "";
452        }
453
454        @Override
455        public boolean isPrototype(final String name) throws NoSuchBeanDefinitionException
456        {
457                return !isSingleton(name);
458        }
459
460        @Override
461        public boolean isTypeMatch(String s, ResolvableType resolvableType) throws NoSuchBeanDefinitionException
462        {
463                return false;
464        }
465
466        @Override
467        @SuppressWarnings({ "unchecked" })
468        public boolean isTypeMatch(final String name, final Class targetType)
469                throws NoSuchBeanDefinitionException
470        {
471                throw new UnsupportedOperationException();
472        }
473
474        @Override
475        public Environment getEnvironment()
476        {
477                return null;
478        }
479}