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        private <A extends Annotation> A findAnnotationOnClass(Class<?> cls, Class<A> annotationType)
201        {
202                // lookup annotation type on class
203                A annotation = cls.getAnnotation(annotationType);
204
205                // lookup annotation type on superclass
206                if (annotation == null && cls.getSuperclass() != null)
207                {
208                        annotation = findAnnotationOnClass(cls.getSuperclass(), annotationType);
209                }
210
211                // lookup annotation type on interfaces
212                if (annotation == null)
213                {
214                        for (Class<?> intfClass : cls.getInterfaces())
215                        {
216                                annotation = findAnnotationOnClass(intfClass, annotationType);
217
218                                if (annotation != null)
219                                {
220                                        break;
221                                }
222                        }
223                }
224
225                return annotation;
226        }
227
228        @Override
229        public ApplicationContext getParent()
230        {
231                throw new UnsupportedOperationException();
232        }
233
234        @Override
235        public String getDisplayName()
236        {
237                throw new UnsupportedOperationException();
238        }
239
240        @Override
241        public long getStartupDate()
242        {
243                throw new UnsupportedOperationException();
244        }
245
246        @Override
247        public void publishEvent(final ApplicationEvent event)
248        {
249                throw new UnsupportedOperationException();
250        }
251
252        @Override
253        public void publishEvent(Object o)
254        {
255                throw new UnsupportedOperationException();
256        }
257
258        @Override
259        public boolean containsBeanDefinition(final String beanName)
260        {
261                return containsBean(beanName);
262        }
263
264        @Override
265        public int getBeanDefinitionCount()
266        {
267                return beans.size();
268        }
269
270        @Override
271        public String[] getBeanDefinitionNames()
272        {
273                return beans.keySet().toArray(new String[0]);
274        }
275
276        @Override
277        public <T> ObjectProvider<T> getBeanProvider(final Class<T> aClass, final boolean b) {
278                return null;
279        }
280
281        @Override
282        public <T> ObjectProvider<T> getBeanProvider(final ResolvableType resolvableType, final boolean b) {
283                return null;
284        }
285
286        @Override
287        public String[] getBeanNamesForType(ResolvableType resolvableType)
288        {
289                return new String[0];
290        }
291
292        @Override
293        public String[] getBeanNamesForType(ResolvableType resolvableType, boolean includeNonSingletons, boolean allowEagerInit)
294        {
295                return new String[0];
296        }
297
298        @Override
299        @SuppressWarnings({ "unchecked" })
300        public String[] getBeanNamesForType(final Class type)
301        {
302                ArrayList<String> names = new ArrayList<>();
303                for (Entry<String, Object> entry : beans.entrySet())
304                {
305                        Object bean = entry.getValue();
306
307                        if (type.isAssignableFrom(bean.getClass()))
308                        {
309                                names.add(entry.getKey());
310                        }
311                }
312                return names.toArray(new String[names.size()]);
313        }
314
315        @Override
316        @SuppressWarnings({ "unchecked" })
317        public String[] getBeanNamesForType(Class type, boolean includeNonSingletons,
318                boolean allowEagerInit)
319        {
320                throw new UnsupportedOperationException();
321        }
322
323        @Override
324        public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons,
325                boolean allowEagerInit) throws BeansException
326        {
327                throw new UnsupportedOperationException();
328        }
329
330        @Override
331        public String[] getBeanNamesForAnnotation(Class<? extends Annotation> aClass)
332        {
333                throw new UnsupportedOperationException();
334        }
335
336        @Override
337        public boolean containsBean(final String name)
338        {
339                return beans.containsKey(name);
340        }
341
342        @Override
343        public boolean isSingleton(final String name) throws NoSuchBeanDefinitionException
344        {
345                return true;
346        }
347
348        @Override
349        public Class<?> getType(final String name) throws NoSuchBeanDefinitionException
350        {
351                return getType(name, true);
352        }
353
354        @Override
355        public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException
356        {
357                Object bean = beans.get(name);
358                if (bean == null)
359                {
360                        throw new NoSuchBeanDefinitionException("No bean with name '" + name + "'");
361                }
362
363                if (bean instanceof FactoryBean)
364                {
365                        return ((FactoryBean) bean).getObjectType();
366                }
367                
368                return bean.getClass();
369        }
370
371        @Override
372        public String[] getAliases(final String name) throws NoSuchBeanDefinitionException
373        {
374                throw new UnsupportedOperationException();
375        }
376
377        /**
378         * @see org.springframework.beans.factory.HierarchicalBeanFactory#getParentBeanFactory()
379         */
380        @Override
381        public BeanFactory getParentBeanFactory()
382        {
383                return null;
384        }
385
386        @Override
387        public String getMessage(final String code, final Object[] args, final String defaultMessage,
388                final Locale locale)
389        {
390                throw new UnsupportedOperationException();
391        }
392
393        @Override
394        public String getMessage(final String code, final Object[] args, final Locale locale)
395                throws NoSuchMessageException
396        {
397                throw new UnsupportedOperationException();
398        }
399
400        @Override
401        public String getMessage(final MessageSourceResolvable resolvable, final Locale locale)
402                throws NoSuchMessageException
403        {
404                throw new UnsupportedOperationException();
405        }
406
407        @Override
408        public Resource[] getResources(final String locationPattern) throws IOException
409        {
410                throw new UnsupportedOperationException();
411        }
412
413        @Override
414        public Resource getResource(final String location)
415        {
416                throw new UnsupportedOperationException();
417        }
418
419        @Override
420        public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException
421        {
422                throw new UnsupportedOperationException();
423        }
424
425        @Override
426        public boolean containsLocalBean(final String arg0)
427        {
428                throw new UnsupportedOperationException();
429        }
430
431        @Override
432        public ClassLoader getClassLoader()
433        {
434                throw new UnsupportedOperationException();
435        }
436
437        @Override
438        public String getId()
439        {
440                return null;
441        }
442
443        @Override
444        public String getApplicationName()
445        {
446                return "";
447        }
448
449        @Override
450        public boolean isPrototype(final String name) throws NoSuchBeanDefinitionException
451        {
452                return !isSingleton(name);
453        }
454
455        @Override
456        public boolean isTypeMatch(String s, ResolvableType resolvableType) throws NoSuchBeanDefinitionException
457        {
458                return false;
459        }
460
461        @Override
462        @SuppressWarnings({ "unchecked" })
463        public boolean isTypeMatch(final String name, final Class targetType)
464                throws NoSuchBeanDefinitionException
465        {
466                throw new UnsupportedOperationException();
467        }
468
469        @Override
470        public Environment getEnvironment()
471        {
472                return null;
473        }
474}