001/*
002 * Copyright 2006-2007 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.batch.core.configuration.support;
018
019import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
020import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
021import org.springframework.beans.factory.support.DefaultListableBeanFactory;
022import org.springframework.context.ApplicationContext;
023import org.springframework.context.ConfigurableApplicationContext;
024import org.springframework.context.annotation.AnnotationConfigApplicationContext;
025import org.springframework.context.support.GenericApplicationContext;
026import org.springframework.context.support.GenericXmlApplicationContext;
027import org.springframework.core.io.Resource;
028import org.springframework.util.Assert;
029import org.springframework.util.StringUtils;
030
031import java.io.IOException;
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.List;
035
036/**
037 * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create.
038 * When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child
039 * context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that
040 * it is closed when it is no longer needed.
041 * 
042 */
043public class GenericApplicationContextFactory extends AbstractApplicationContextFactory {
044
045        /**
046         * Create an application context factory for the resource specified. The resource can be an actual {@link Resource},
047         * in which case it will be interpreted as an XML file, or it can be a @Configuration class, or a package name.
048         * All types must be the same (mixing XML with a java package for example is not allowed and will result in an
049         * {@link java.lang.IllegalArgumentException}).
050         * 
051         * @param resources some resources (XML configuration files, @Configuration classes or java packages to scan)
052         */
053        public GenericApplicationContextFactory(Object... resources) {
054                super(resources);
055        }
056
057        /**
058         * @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object...)
059         */
060        @Override
061        protected ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
062                        Object... resources) {
063                ConfigurableApplicationContext context;
064
065                if (allObjectsOfType(resources, Resource.class)) {
066                         context = new ResourceXmlApplicationContext(parent, resources);
067                } else if (allObjectsOfType(resources, Class.class)) {
068                         context =  new ResourceAnnotationApplicationContext(parent, resources);
069                } else if (allObjectsOfType(resources, String.class)) {
070                         context = new ResourceAnnotationApplicationContext(parent, resources);
071                } else {
072                        List<Class<?>> types = new ArrayList<Class<?>>();
073                        for (Object resource : resources) {
074                                types.add(resource.getClass());
075                        }
076                        throw new IllegalArgumentException("No application context could be created for resource types: "
077                                                                                                           + Arrays.toString(types.toArray()));
078                }
079
080                return context;
081        }
082        
083        private boolean allObjectsOfType(Object[] objects, Class<?> type) {
084                for (Object object : objects) {
085                        if (!type.isInstance(object)) {
086                                return false;
087                        }
088                }
089                return true;
090        }
091
092        private abstract class ApplicationContextHelper {
093
094                private final DefaultListableBeanFactory parentBeanFactory;
095
096                private final ConfigurableApplicationContext parent;
097
098                public ApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context,
099                                Object... config) {
100                        this.parent = parent;
101                        if (parent != null) {
102                                Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory,
103                                                "The parent application context must have a bean factory of type DefaultListableBeanFactory");
104                                parentBeanFactory = (DefaultListableBeanFactory) parent.getBeanFactory();
105                        }
106                        else {
107                                parentBeanFactory = null;
108                        }
109                        context.setParent(parent);
110                        context.setId(generateId(config));
111                        loadConfiguration(config);
112                        prepareContext(parent, context);
113                }
114
115                protected abstract String generateId(Object... configs);
116
117                protected abstract void loadConfiguration(Object... configs);
118
119                protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
120                        if (parentBeanFactory != null) {
121                                GenericApplicationContextFactory.this.prepareBeanFactory(parentBeanFactory, beanFactory);
122                                for (Class<? extends BeanFactoryPostProcessor> cls : getBeanFactoryPostProcessorClasses()) {
123                                        for (String name : parent.getBeanNamesForType(cls)) {
124                                                beanFactory.registerSingleton(name, (parent.getBean(name)));
125                                        }
126                                }
127                        }
128                }
129
130        }
131
132        private final class ResourceXmlApplicationContext extends GenericXmlApplicationContext {
133
134                private final ApplicationContextHelper helper;
135
136                /**
137                 * @param parent
138                 */
139                public ResourceXmlApplicationContext(ConfigurableApplicationContext parent, Object... resources) {
140
141                        class ResourceXmlApplicationContextHelper extends ApplicationContextHelper {
142
143                                ResourceXmlApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context, Object... config) {
144                                        super(parent, context, config);
145                                }
146
147                                @Override
148                                protected String generateId(Object... configs) {
149                                        Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class);
150                                        try {
151                                                List<String> uris = new ArrayList<String>();
152                                                for (Resource resource : resources) {
153                                                        uris.add(resource.getURI().toString());
154                                                }
155                                                return StringUtils.collectionToCommaDelimitedString(uris);
156                                        }
157                                        catch (IOException e) {
158                                                return Arrays.toString(resources);
159                                        }
160                                }
161                                @Override
162                                protected void loadConfiguration(Object... configs) {
163                                        Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class);
164                                        load(resources);
165                                }
166                        }
167                        helper = new ResourceXmlApplicationContextHelper(parent, this, resources);
168                        refresh();
169                }
170
171                @Override
172                protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
173                        super.prepareBeanFactory(beanFactory);
174                        helper.prepareBeanFactory(beanFactory);
175                }
176
177                @Override
178                public String toString() {
179                        return "ResourceXmlApplicationContext:" + getId();
180                }
181
182        }
183
184        private final class ResourceAnnotationApplicationContext extends AnnotationConfigApplicationContext {
185
186                private final ApplicationContextHelper helper;
187
188                public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object... resources) {
189
190                        class ResourceAnnotationApplicationContextHelper extends ApplicationContextHelper {
191
192                                public ResourceAnnotationApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context, Object... config) {
193                                        super(parent, context, config);
194                                }
195
196                                @Override
197                                protected String generateId(Object... configs) {
198                                        if (allObjectsOfType(configs, Class.class)) {
199                                                Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class);
200                                                List<String> names = new ArrayList<String>();
201                                                for (Class<?> type : types) {
202                                                        names.add(type.getName());
203                                                }
204                                                return StringUtils.collectionToCommaDelimitedString(names);
205                                        }
206                                        else {
207                                                return Arrays.toString(configs);
208                                        }
209                                }
210                                @Override
211                                protected void loadConfiguration(Object... configs) {
212                                        if (allObjectsOfType(configs, Class.class)) {
213                                                Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class);
214                                                register(types);
215                                        }
216                                        else {
217                                                String[] pkgs = Arrays.copyOfRange(configs, 0, configs.length, String[].class);
218                                                scan(pkgs);
219                                        }
220                                }
221                        }
222                        helper = new ResourceAnnotationApplicationContextHelper(parent, this, resources);
223                        refresh();
224                }
225
226                @Override
227                protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
228                        super.prepareBeanFactory(beanFactory);
229                        helper.prepareBeanFactory(beanFactory);
230                }
231
232                @Override
233                public String toString() {
234                        return "ResourceAnnotationApplicationContext:" + getId();
235                }
236
237        }
238
239}