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